How to understand processing main method in other IDE given multi classes defined inside?

edited November 2015 in Questions about Code

Hi everyone, I want to learn to write processing code in other IDEs like Eclipse or Netbeans (thereafter IDE). To learn it I try to work backward by running and study processing application exported java file (let's call it RainGame.java) inside other IDEs.

I try to run the java file directly inside IDE, it works ( imported core.jar of course); then I cut the following code from RainGame.java and copied it into a new class named Main, and then run the main method of Main class, it still works:

  import processing.core.*; 
  public class Main extends PApplet{ 
      static public void main(String[] passedArgs) {
        String[] appletArgs = new String[] { "exercise_10_04_improved_rain_game" };
        if (passedArgs != null) {
          PApplet.main(concat(appletArgs, passedArgs));
        } else {
          PApplet.main(appletArgs);
        }
      }
   }

However, when I tried to cut three classes inside RainGame.java out and copied them into three class files named Catcher, Drop, Timer, and then call main method in Main class again, but they didn't work. The three classes are wrapped as following:

    import processing.core.*;
    public class Catcher extends PApplet {
    ....
    }
    import processing.core.*;
    public class Drop extends PApplet {
    ...
    }
    import processing.core.*;
    public class Timer extends PApplet {
    ...
    }

My Question: why they don't work anymore? I wonder it is due to the mechanism of main method and the use of passedArgs and appletArgs, but I have no idea how they work and I could not find anything useful online to figure it out.

Thanks for your help

Answers

  • What exactly do you mean when you say they don't work? Do you get an error? Unexpected behavior? Something else?

  • @KevinWorkman Thanks and here are the errors:

        Exception in thread "Animation Thread" java.lang.NullPointerException
            at processing.core.PApplet.stroke(PApplet.java:13759)
            at Catcher.display(Catcher.java:31)
            at RainGame.draw(RainGame.java:67)
            at processing.core.PApplet.handleDraw(PApplet.java:2399)
            at processing.awt.PSurfaceAWT$12.callDraw(PSurfaceAWT.java:1527)
            at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:316)
    
  • @GoToLoop thanks. Though I found one post which seemed closed related, it looked a little too advanced compared with my problem at hand. So I still couldn't figure out by reading those posts. Would you explain in a simpler way at your convenience? Thanks

  • I found the API for main method

         The simplest way to turn and applet into an application is to add the following code to your program:
    
        static public void main(String args[]) {
           PApplet.main("YourSketchName", args);
         }
    

    Now, I guess what troubles me the most is what exactly are the args, what they are for, and how to use them in the main method?

    Thanks

  • edited November 2015
  • You need to modify the public void static main method for each class e.g.

    // Main class
    public static void main(String args[]) {
        PApplet.main(new String[] { Main.class.getName() } );
    }
    

    // Catcher class
    public static void main(String args[]) {
        PApplet.main(new String[] { Catcher.class.getName() } );
    }
    

    // Drop class
    public static void main(String args[]) {
        PApplet.main(new String[] { Drop.class.getName() } );
    }
    

    // RainGame class
    public static void main(String args[]) {
        PApplet.main(new String[] { RainGame.class.getName() } );
    }
    

    The public void static main method defines the starting point for program execution. That means you usually have just one main method per application.

    Personally I would avoid calling a class Main as it can cause confusion. ;)

  • edited November 2015

    Just an observation: Saying something like RainGame.class.getName() is completely verbose & redundant.
    A simple & direct "RainGame" is the way to go! ;)

  • edited November 2015

    And rather than: L-)

    class RainGame extends PApplet {
      public static void main(String[] args) {
          main(new String[] { "RainGame" });
      }
    }
    

    This overloaded main() "igniter" is more direct & concise: $-)

    class RainGame extends PApplet {
      public static void main(String[] args) {
          main("RainGame", args);
      }
    }
    
  • Just an observation: Saying something like RainGame.class.getName() is completely verbose & redundant.

    You might want to rethink that observation since it is wrong ;)

    The OP is asking about writing sketches in Eclipse and Netbeans which both have the facility to refactor-rename classes etc. In my example changing the class name will also change the statement inside the main method. In your example the user would have to remember to change the "RainGame" string.

    Verbosity is not always undesirable :)

  • Verbosity is not always undesirable

    Here's a nice couple of quotes for @GoToLoop I saw in Haverbeke's Eloquent Javascript; both of which made me think of his coding style :P

    “Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” —Brian Kernighan and P.J. Plauger, The Elements of Programming Style

    This one's good as well:

    “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.” —C.A.R. Hoare, 1980 ACM Turing Award Lecture

  • edited November 2015

    @blindfish, those quotes you've posted above and that RainGame.class.getName() "pattern" got nothing to do w/ ea. other at all!

    As @quark had already explained, that is an advanced refactoring facility provided by advanced IDEs.

    It's got nothing to do w/ clarity nor it is something provided by the Java language itself! 8-|

    Typing in "RainGame" is both clearer and faster for the point-of-view of Java language alone! ~O)

    Actually for beginners, RainGame.class.getName() is a completely obscure statement! 8-}

  • @GoToLoop: Sorry - I did indeed stray well off topic :\"> - but those quotes followed nicely from @quark's "Verbosity is not always undesirable"... O:-)

  • edited November 2015

    @quark and @GoToLoop thanks a lot!

    I tried the following options: 1. giving each class a main method; 2. only give main method to RainGame class and run from there; however, neither works. Maybe it is due to my misunderstanding of @quark's answer.

    I have pasted the full code below. The application I call it CatchRainGame which consists of five classes: 1. Main class only contains the main method for the application; 2. RainGame class contains global variables, settings(), setup(), draw() and etc.; 3. Drop class is responsible for creating rain drop object; 4. Timer for counting; 5. Catcher class is for displaying a catcher object to catch rain drop.

    My intention is to run the application from the main method of Main class. So, I would imagine there is only one main method as @quark suggested and I want the only main method in a seperated class Main, away from RainGame, Drop, Timer, Catcher classes.

    What exactly should I do to the codes below? Thanks

    The original code was from a book example from Learning Processing by Daniel Shiffman.

    Main class code --

        import processing.core.*; 
    
        public class Main extends PApplet
        {
            static public void main(String[] passedArgs) {
    
                PApplet.main(new String[] { RainGame.class.getName() } );
            }
        }
    

    RainGame class code --

        import processing.core.*; 
    
    
        public class RainGame extends PApplet {
    
    
        Catcher catcher;    // One catcher object
        Timer timer;        // One timer object
        Drop[] drops;       // An array of drop objects
        int totalDrops = 0; // totalDrops
    
        // A boolean to let us know if the game is over
        boolean gameOver = false;
    
        // Variables to keep track of score, level, lives left
        int score = 0;      // User's score
        int level = 1;      // What level are we on
        int lives = 10;     // 10 lives per level (10 raindrops can hit the bottom)
        int levelCounter = 0;
    
        PFont f;
    
        @ Override
        public void setup() {
    
          catcher = new Catcher(32); // Create the catcher with a radius of 32
          drops = new Drop[50];      // Create 50 spots in the array (each level now just has 25 drops)
          timer = new Timer(300);    // Create a timer that goes off every 300 milliseconds
          timer.start();             // Starting the timer
    
          f = createFont("Arial", 12, true); // A font to write text on the screen
        }
    
        @ Override
        public void draw() {
          background(255);
    
          // If the game is over
          if (gameOver) {
            textFont(f, 48);
            textAlign(CENTER);
            fill(0);
            text("GAME OVER", width/2, height/2);
          } else {
    
            // Set catcher location
            catcher.setLocation(mouseX, mouseY); 
            // Display the catcher
            catcher.display(); 
    
            // Check the timer
            if (timer.isFinished()) {  // at start, there is no drop, give time for player to prepare
              // Deal with raindrops
              // Initialize one drop
              if (totalDrops < drops.length) {  // make sure there are specific num of drops on screen
                drops[totalDrops] = new Drop();
                // Increment totalDrops
                totalDrops++;
              }
              timer.start();  // restart time for next isFinished test
            }
    
            // Move and display all drops
            for (int i = 0; i < totalDrops; i++ ) {
              if (!drops[i].finished) {
                drops[i].move();
                drops[i].display();
                if (drops[i].reachedBottom()) {
                  levelCounter++;
                  drops[i].finished(); 
                  // If the drop reaches the bottom a live is lost
                  lives--;
                  // If lives reach 0 the game is over
                  if (lives <= 0) {
                    gameOver = true;
                  }
                } 
    
                // Everytime you catch a drop, the score goes up
                if (catcher.intersect(drops[i])) {
                  drops[i].finished();
                  levelCounter++;
                  score++;
                }
              }
            }
    
    
            // If all the drops are done, that leve is over!
            if (levelCounter >= drops.length) {
              // Go up a level
              level++;
              // Reset all game elements
              levelCounter = 0;
              lives = 10;
              timer.setTime(constrain(300-level*25, 0, 300)); // level up, break time gets shorter, 
                                                // but no short than 0, no longer than 300
              totalDrops = 0;
            }
    
            // Display number of lives left
            textFont(f, 14);
            fill(0);
            text("Lives left: " + lives, 10, 20);
            rect(10, 24, lives*10, 10);  // life/health block length
    
            text("Level: " + level, 300, 20);
            text("Score: " + score, 300, 40);
          }
        }
    
          public void settings() {  size(640, 360); }
    
        //  public static void main(String args[]) {
        //    PApplet.main(new String[] { RainGame.class.getName() } );
        //  }
        }
    

    Drop class code --

            import processing.core.*;
            public class Drop extends PApplet
            {
    
    
              float x, y;   // Variables for location of raindrop
              float speed;  // Speed of raindrop
              int c;
              float r;      // Radius of raindrop
    
              // New variable to keep track of whether drop is still being used
              boolean finished = false;
    
              Drop() {
                r = 8;                   // All raindrops are the same size
                x = random(width);       // Start with a random x location
                y = -r*4;                // Start a little above the window
                speed = random(2, 5);    // Pick a random speed
                c = color(50, 100, 150); // Color
              }
    
              // Move the raindrop down
              public void move() {
                // Increment by speed
                y += speed;
              }
    
              // Check if it hits the bottom
              public boolean reachedBottom() {
                // If we go a little beyond the bottom
                // because y start from a place where has a distance
                // from bottom = -r*4 = height + 4*r
                if (y > height + r*4) { 
                  return true;
                } else {
                  return false;
                }
              }
    
              // Display the raindrop
              public void display() {
                // Display the drop
                fill(c);
                noStroke();
                for (int i = 2; i < r; i++) {
                  ellipse(x, y - r*4 + i*4, i*2, i*2);
                }
              }
    
              // If the drop is caught
              public void finished() {
                finished = true;
              }
    
            //  public static void main(String args[]) {
            //    PApplet.main(new String[] { Drop.class.getName() } );
            //  }
    
            }
    

    Timer class code --

        import processing.core.*;
        public class Timer extends PApplet
        {
    
    
          int savedTime; // When Timer started
          int totalTime; // How long Timer should last
    
          Timer(int tempTotalTime) {
            totalTime = tempTotalTime;
          }
    
          public void setTime(int t) {
            totalTime = t;
          }
    
          // Starting the timer
          public void start() {
            // When the timer starts it stores the current time in milliseconds.
            savedTime = millis();
          }
    
          // The function isFinished() returns true if 5,000 ms have passed. 
          // The work of the timer is farmed out to this method.
          public boolean isFinished() { 
            // Check how much time has passed
            int passedTime = millis()- savedTime;
            if (passedTime > totalTime) {
              return true;
            } else {
              return false;
            }
          }
    
        //  public static void main(String args[]) {
        //    PApplet.main(new String[] { Timer.class.getName() } );
        //  }
    
        }
    

    Catcher class code --

            import processing.core.*;
            public class Catcher extends PApplet
            {
    
                float r;    // radius
                int col;  // color
                float x, y; // location
    
                Catcher(float tempR) {
                    r = tempR;
                    col = color(50, 10, 10, 150);
                    x = 0;
                    y = 0;
                }
    
                public void setLocation(float tempX, float tempY) {
                    x = tempX;
                    y = tempY;
                }
    
                public void display() {
                    stroke(0);
                    fill(col);
                    ellipse(x, y, r*2, r*2);
                }
    
                // A function that returns true or false based on
                // if the catcher intersects a raindrop
                public boolean intersect(Drop d) {
                    // Calculate distance
                    float distance = dist(x, y, d.x, d.y); 
    
                    // Compare distance to sum of radii
                    if (distance < r + d.r) { 
                        return true;
                    } else {
                        return false;
                    }
    
                }
    
            //    public static void main(String args[]) {
            //        PApplet.main(new String[] { Catcher.class.getName() } );
            //    }
    
            }
    
  • edited November 2015 Answer ✓
    • Apart from RainGame class, all the others don't have PApplet callbacks.
    • Therefore they shouldn't be "ignited" and have separate canvas. They all share RainGame's.
    • Actually, they shouldn't extend PApplet at all!
    • Rather demand RainGame's reference passed to their constructors so they too can use its canvas.
  • edited November 2015

    @GoToLoop thanks, but when I removed extends PApplet from other three classes, they won't recognize width, height, ellipse(). what shall I do about this?

    Also, what do you mean by PApplet callbacks? What exactly they are inside RainGame class?

  • edited November 2015

    @GoToLoop thanks, I went back to Processing in Eclipse with Multiple Classes and it says how and why need to remove extends PApplet.

    Now, I have corrected my code accordingly, this time there is no error, but I still could not get my canvas and the game. I am not sure where goes wrong.

    In the output it says:

        run:
        Usage: PApplet [options] <class name> [sketch args]
        See the Javadoc for PApplet for an explanation.
        Java Result: 1
        BUILD SUCCESSFUL (total time: 0 seconds)
    

    The corrected codes are below:

    Main class --

        import processing.core.*;
    
        public class MainToRun
        {
            static public void main(String[] passedArgs) {
    
                PApplet.main(new String[] { RainGame.class.getName() } );
        //        RainGame.main("test.RainGame");
            }
        }
    

    RainGame --

        import processing.core.*; 
    
    
        public class RainGame extends PApplet {
    
    
        Catcher catcher;    // One catcher object
        Timer timer;        // One timer object
        Drop[] drops;       // An array of drop objects
        int totalDrops = 0; // totalDrops
    
        // A boolean to let us know if the game is over
        boolean gameOver = false;
    
        // Variables to keep track of score, level, lives left
        int score = 0;      // User's score
        int level = 1;      // What level are we on
        int lives = 10;     // 10 lives per level (10 raindrops can hit the bottom)
        int levelCounter = 0;
    
        PFont f;
    
        @ Override
        public void setup() {
    
          catcher = new Catcher(this, 32); // Create the catcher with a radius of 32
          drops = new Drop[50];      // Create 50 spots in the array (each level now just has 25 drops)
          timer = new Timer(this, 300);    // Create a timer that goes off every 300 milliseconds
          timer.start();             // Starting the timer
    
          f = createFont("Arial", 12, true); // A font to write text on the screen
        }
    
        @ Override
        public void draw() {
          background(255);
    
          // If the game is over
          if (gameOver) {
            textFont(f, 48);
            textAlign(CENTER);
            fill(0);
            text("GAME OVER", width/2, height/2);
          } else {
    
            // Set catcher location
            catcher.setLocation(mouseX, mouseY); 
            // Display the catcher
            catcher.display(); 
    
            // Check the timer
            if (timer.isFinished()) {  // at start, there is no drop, give time for player to prepare
              // Deal with raindrops
              // Initialize one drop
              if (totalDrops < drops.length) {  // make sure there are specific num of drops on screen
                drops[totalDrops] = new Drop(this);
                // Increment totalDrops
                totalDrops++;
              }
              timer.start();  // restart time for next isFinished test
            }
    
            // Move and display all drops
            for (int i = 0; i < totalDrops; i++ ) {
              if (!drops[i].finished) {
                drops[i].move();
                drops[i].display();
                if (drops[i].reachedBottom()) {
                  levelCounter++;
                  drops[i].finished(); 
                  // If the drop reaches the bottom a live is lost
                  lives--;
                  // If lives reach 0 the game is over
                  if (lives <= 0) {
                    gameOver = true;
                  }
                } 
    
                // Everytime you catch a drop, the score goes up
                if (catcher.intersect(drops[i])) {
                  drops[i].finished();
                  levelCounter++;
                  score++;
                }
              }
            }
    
    
            // If all the drops are done, that leve is over!
            if (levelCounter >= drops.length) {
              // Go up a level
              level++;
              // Reset all game elements
              levelCounter = 0;
              lives = 10;
              timer.setTime(constrain(300-level*25, 0, 300)); // level up, break time gets shorter, 
                                                // but no short than 0, no longer than 300
              totalDrops = 0;
            }
    
            // Display number of lives left
            textFont(f, 14);
            fill(0);
            text("Lives left: " + lives, 10, 20);
            rect(10, 24, lives*10, 10);  // life/health block length
    
            text("Level: " + level, 300, 20);
            text("Score: " + score, 300, 40);
          }
        }
    
          public void settings() {  size(640, 360); }
    
    
        }
    

    Drop --

        import processing.core.*;
        public class Drop
        {
    
    
          float x, y;   // Variables for location of raindrop
          float speed;  // Speed of raindrop
          int c;
          float r;      // Radius of raindrop
          PApplet parent;
    
          // New variable to keep track of whether drop is still being used
          boolean finished = false;
    
          Drop(PApplet p) {
            parent = p;
            r = 8;                   // All raindrops are the same size
            x = parent.random(parent.width);       // Start with a random x location
            y = -r*4;                // Start a little above the window
            speed = parent.random(2, 5);    // Pick a random speed
            c = parent.color(50, 100, 150); // Color
          }
    
          // Move the raindrop down
          public void move() {
            // Increment by speed
            y += speed;
          }
    
          // Check if it hits the bottom
          public boolean reachedBottom() {
            // If we go a little beyond the bottom
            // because y start from a place where has a distance
            // from bottom = -r*4 = height + 4*r
            if (y > parent.height + r*4) { 
              return true;
            } else {
              return false;
            }
          }
    
          // Display the raindrop
          public void display() {
            // Display the drop
            parent.fill(c);
            parent.noStroke();
            for (int i = 2; i < r; i++) {
              parent.ellipse(x, y - r*4 + i*4, i*2, i*2);
            }
          }
    
          // If the drop is caught
          public void finished() {
            finished = true;
          }
    
    
        }
    

    Timer --

        import processing.core.*;
        public class Timer
        {
    
    
          int savedTime; // When Timer started
          int totalTime; // How long Timer should last
          PApplet parent;
    
    
          Timer(PApplet p, int tempTotalTime) {
            parent = p;
            totalTime = tempTotalTime;
          }
    
          public void setTime(int t) {
            totalTime = t;
          }
    
          // Starting the timer
          public void start() {
            // When the timer starts it stores the current time in milliseconds.
            savedTime = parent.millis();
          }
    
          // The function isFinished() returns true if 5,000 ms have passed. 
          // The work of the timer is farmed out to this method.
          public boolean isFinished() { 
            // Check how much time has passed
            int passedTime = parent.millis()- savedTime;
            if (passedTime > totalTime) {
              return true;
            } else {
              return false;
            }
          }
    
    
    
        }
    

    Catcher --

        import processing.core.*;
        public class Catcher
        {
    
            float r;    // radius
            int col;  // color
            float x, y; // location
            PApplet parent;
    
            Catcher(PApplet p, float tempR) {
                parent = p;
                r = tempR;
                col = parent.color(50, 10, 10, 150);
                x = 0;
                y = 0;
            }
    
            public void setLocation(float tempX, float tempY) {
                x = tempX;
                y = tempY;
            }
    
            public void display() {
                parent.stroke(0);
                parent.fill(col);
                parent.ellipse(x, y, r*2, r*2);
            }
    
            // A function that returns true or false based on
            // if the catcher intersects a raindrop
            public boolean intersect(Drop d) {
                // Calculate distance
                float distance = parent.dist(x, y, d.x, d.y); 
    
                // Compare distance to sum of radii
                if (distance < r + d.r) { 
                    return true;
                } else {
                    return false;
                }
    
            }
    
    
    
        }
    
  • edited November 2015
    • Nice you've figured it out! But please don't use a big name to keep the PApplet's reference.
    • Use something like p, _, $ or some other 1-character name. :P
    • Some Processing callbacks: setup(), draw(), mousePressed(), keyReleased(), etc.

    P.S.: Strange that https://Processing.org/tutorials/eclipse/ isn't listed in https://Processing.org/tutorials/ :-$

  • edited November 2015

    ... this time there is no error, but I still could not get my canvas and the game.

    Me neither! B/c after copy & paste your code in Processing 3, each class in its own ".java" tab, it simply worked. Only small adjustment I had to do was paste MainToRun in the main ".pde" this way instead:

    static public void main(String[] passedArgs) {
      PApplet.main(new String[] { RainGame.class.getName() });
    }
    

    Everything else I didn't have to modify a single dot in order to play the game. <:-P

  • anyway, I work around this by working into two IDEs at the same time. Thanks everyone!

  • @quark and @GoToLoop, however, I still wonder as we repeatedly seen args:

    What could args or passedArgs be?
    As they are args[] or passedArgs[], does it mean there can be multiple strings applied in args or passedArgs? Could you give some simple code examples?

    Thanks!

    From processing API -- we have seen

        static public void main(String args[]) {
           PApplet.main("YourSketchName", args);
         }
    

    From the example code above -- we have seen

        public class Main extends PApplet
        {
            static public void main(String[] passedArgs) {
    
    
                String[] appletArgs = new String[] { "RainGame" };
                if (passedArgs != null) {
                    PApplet.main(concat(appletArgs, passedArgs));
                } else {
                    PApplet.main(appletArgs);
                }
    
    
            }
        }
    
  • edited November 2015 Answer ✓

    This is what I've said far way up:

    For 99.9% of cases we can ignore args. Dunno n1 who passes commandline parameters to Processing sketches after all!

    Java's own main() parameter comes from either command line or shortcut arguments.
    PApplet.main() in turn has extra features which I've already mentioned as well:

    Like "--display=", "--location=", "--sketch-path=", etc.

    Correct order to fully merge Java's main() + PApplet.main()'s arguments is something like this:

    static public void main(String[] args) {
      String[] p5Args = { "--location=300,100", "--bgcolor=#ffa09a" };
      String className = Thread.currentThread().getStackTrace()[1].getClassName();
      String[] mergedArgs = concat(append(p5Args, className), args);
    
      PApplet.main(mergedArgs);
      printArray(mergedArgs);
    }
    

    But as I've said, args is almost always empty.
    No1 calls Processing sketches from commandline passing args to it! 3:-O

  • edited November 2015

    BtW, in case args happen to be passed, and by using the model above, we can see those stored in variable args[] inside sketch: :-bd

    public void setup() {
      if (args != null) {
        println("\nargs len:", args.length);
        printArray(args);
      } else println("\nNo args was passed now!");
    }
    
  • edited November 2015

    But those are just advanced examples. As Processing PApplet.main()'s description says: :D

    The simplest way to turn an applet into an application is to add the following code to your program:

    static public void main(String[] args) {
      String className = Thread.currentThread().getStackTrace()[1].getClassName();
      main(className, args);
    }
    
  • edited November 2015

    @GoToLoop

    But please don't use a big name to keep the PApplet's reference. Use something like p, _, $ or some other 1-character name.

    Saving a few key presses is not enough reason to go against good programming practice which recommends that identifiers should have meaningful names. I think it is important that we promote good programming practice on this forum, especially for newbies.

    Actually for beginners, RainGame.class.getName() is a completely obscure statement! and of course

    String className = Thread.currentThread().getStackTrace()[1].getClassName();

    is so much clearer ;))

    @blindfish
    Glad you liked the quote

    "Verbosity is not always undesirable"

    here is two more for you

    "Brevity is not always desirable"
    "Clarity of meaning is always desirable"

    I liked reading the quotes you posted. Although they were not on target here, but it doesn't take away their truth :)

  • edited November 2015

    As Processing programmers we're not used to call its API prefixed w/ anything.
    Having to type in something simple like:
    textFont(createFont("Arial", 12, true)); as parent.textFont(parent.createFont("Arial", 12, true));
    is tedious & tiresome enough! I-)

    Establishing some convention to represent the PApplet's reference as 1-character name won't diminish its clarity any more than using i & j as iterators, x & y & z as coordinates, w & h as dimensions: 8-X
    p.textFont(p.createFont("Arial", 12, true));

    That's just common sense folks! As even p5.js has adopted such convention as well: ;;)
    https://GitHub.com/processing/p5.js/wiki/Instantiation-Cases

    String className = Thread.currentThread().getStackTrace()[1].getClassName(); is so much clearer...

    Of course it isn't! But you were already using a much more verbose, slower and cryptic:
    String className = RainGame.class.getName(); for the sake of your IDE's rename/refactor features.

    I've just gone a little farther and replaced it w/ a snippet not relying on any special IDE for it to work!
    That is, no matter which class name that statement is in, it's gonna find that out on its own! \m/

  • edited November 2015

    r y c i t a t ;)

  • Thanks a million! @GoToLoop and @quark

Sign In or Register to comment.