X

Geertjan's Blog

  • February 3, 2015

Greenfoot and NetBeans

Geertjan Wielenga
Product Manager

When I met with Michael Kölling, from BlueJ and Greenfoot, when we discussed the relationship between those tools and NetBeans, Michael pointed out that BlueJ users are closer to NetBeans IDE than Greenfoot users. I took copious notes of our discussion, especially on the differences between BlueJ and Greenfoot, since that difference has never been completely clear to me. As Michael explained it to me: "BlueJ visualizes program structures and their execution, while Greenfoot uses visualization as the input to the program."

"Today we're building a game," is the angle teachers with Greenfoot in the classroom use, rather than "today we're going to learn about object orientation". Clearly, the two tools, though built on the same framework, have different approaches, with "motivation first" being the approach applicable to Greenfoot. It doesn't try to be as complete and formal as BlueJ and it's not a general IDE, while BlueJ is for teaching full programming. Anything in Java you can do in BlueJ, including Java 8. On the other hand, Greenfoot is just for two dimensional graphics, i.e., a very specific domain. Many programs you would never make in Greenfoot; it sacrificises generality for one thing only, to raise interest in programming. Then, after that, you potentially, assuming Greenfoot has raised your interest, go somewhere else to become a real programmer.

The underlying reasoning for Greenfoot came about upon the discovery that, increasingly, the first contact people have with programming is before university or technical college. Long before that, you've normally already decided whether programming is "for you". To "catch them early", the insight was that you need to enthuse people in the early stages of their development, i.e., you need to go to schools.

Anyway, all that having been said, let's see what it takes to take a Greenfoot project and work with it in NetBeans. It's less intuitive than BlueJ (as described here) but Michael and I worked through a process that works. My plan is to make a NetBeans plugin for converting a newly opened Greenfoot project in NetBeans to being a project that does step 2, 3, and 4 below automatically:

  1. Import Java Source Files. Use the 'Java Project with Existing Sources' wizard in NetBeans to import the Java source files created in Greenfoot, same as for BlueJ.

  2. Configure Classpath. Add the two JARs, greenfoot.jar and bluejcore.jar, from the Greenfoot distribution, to the classpath of the NetBeans project.

  3. Set NetBeans Project Properties. Set the main method to 'greenfoot.export.GreenfootScenarioMain' and set the working directory to point to the location of the sources. Click to enlarge the image below.



  4. Set Greenfoot Project Properties. Create a 'standalone.properties' file in the 'default package', with properties such as 'main.class' and 'project.name':

    project.name=boids
    main.class=Sky
    #scenario.lock=true
    run.once=Act
    controls.run.button=Run
    controls.runonce.shortDescription=Call 'act' once for every actor.
    pause.simulation=Pause
    run.simulation=Run
    controls.run.shortDescription=Run the simulation.
    reset.world=Reset
    controls.reset.shortDescription=Reset the simulation.
    controls.speed.label=Speed\:

  5. Figure Out Images and Resources. Images and resources aren't found automatically. Either put all images and resources into the default package or add 'setImage' method to the classes where images are used and do the same for the resources. Possibly something needs to change within the Greenfoot code to find the images and resources automatically when the Greenfoot JAR is used in standalone mode.

The whole project, here the 'Boids' project, looks something like this:


When you run it from NetBeans, you should see this, which is exactly what you see when you run it from Greenfoot. Take note of some of the properties shown above in step 4, which are applied to the viewer, e.g., the buttons you see in the lower part below:

As you can see from the above, a bit of tweaking must be done, which is why I think a small NetBeans plugin that does the above automatically would be helpful, i.e., what's needed is a "Greenfoot" tab in the Project Properties dialog of Java SE projects, which will let the user specify the above items to be added to the project and configure the project automatically to smoothen the transition from Greenfoot to NetBeans.

Something like this:

Sources: https://github.com/GeertjanWielenga/NetBeans4Greenfoot

Note: See part 2 for enhancements to the above procedure.

Join the discussion

Comments ( 2 )
  • guest Wednesday, April 29, 2015

    hello i am a student in year 2 ,and i have a project to make so i used the sample on netbeans the one with the clock can you help me understant what the code does?because i have to write a project on the aplication.Thank you.

    public void start(Stage primaryStage) {

    package digitalclock;

    import java.util.Calendar;

    import javafx.animation.KeyFrame;

    import javafx.animation.Timeline;

    import javafx.application.Application;

    import javafx.event.ActionEvent;

    import javafx.event.EventHandler;

    import javafx.scene.Group;

    import javafx.scene.Parent;

    import javafx.scene.Scene;

    import javafx.scene.effect.Effect;

    import javafx.scene.effect.Glow;

    import javafx.scene.effect.InnerShadow;

    import javafx.scene.image.Image;

    import javafx.scene.image.ImageView;

    import javafx.scene.paint.Color;

    import javafx.scene.shape.Circle;

    import javafx.scene.shape.Polygon;

    import javafx.scene.transform.Scale;

    import javafx.scene.transform.Shear;

    import javafx.stage.Stage;

    import javafx.util.Duration;

    public class DigitalClock extends Application {

    private Clock clock;

    @Override

    public void start(Stage primaryStage) {

    primaryStage.setTitle("Digital Clock");

    Group root = new Group();

    Scene scene = new Scene(root, 480, 412);

    // add background image

    ImageView background = new ImageView(new Image(getClass().getResourceAsStream("DigitalClock-background.png")));

    // add digital clock

    clock = new Clock(Color.WHITE, Color.rgb(50,50,50));

    clock.setLayoutX(45);

    clock.setLayoutY(186);

    clock.getTransforms().add(new Scale(0.83f, 0.83f, 0, 0));

    // add background and clock to sample

    root.getChildren().addAll(background, clock);

    primaryStage.setScene(scene);

    primaryStage.show();

    }

    public static final class Clock extends Parent {

    private Calendar calendar = Calendar.getInstance();

    private Digit[] digits;

    private Timeline delayTimeline, secondTimeline;

    public Clock(Color onColor, Color offColor) {

    Glow onEffect = new Glow(1.7f);

    onEffect.setInput(new InnerShadow());

    Glow onDotEffect = new Glow(1.7f);

    onDotEffect.setInput(new InnerShadow(5,Color.BLACK));

    InnerShadow offEffect = new InnerShadow();

    digits = new Digit[7];

    for (int i = 0; i < 6; i++) {

    Digit digit = new Digit(onColor, offColor, onEffect, offEffect);

    digit.setLayoutX(i * 80 + ((i + 1) % 2) * 20);

    digits[i] = digit;

    getChildren().add(digit);

    }

    Group dots = new Group(

    new Circle(80 + 54 + 20, 44, 6, onColor),

    new Circle(80 + 54 + 17, 64, 6, onColor),

    new Circle((80 * 3) + 54 + 20, 44, 6, onColor),

    new Circle((80 * 3) + 54 + 17, 64, 6, onColor));

    dots.setEffect(onDotEffect);

    getChildren().add(dots);

    refreshClocks();

    play();

    }

    private void refreshClocks() {

    calendar.setTimeInMillis(System.currentTimeMillis());

    int hours = calendar.get(Calendar.HOUR_OF_DAY);

    int minutes = calendar.get(Calendar.MINUTE);

    int seconds = calendar.get(Calendar.SECOND);

    digits[0].showNumber(hours / 10);

    digits[1].showNumber(hours % 10);

    digits[2].showNumber(minutes / 10);

    digits[3].showNumber(minutes % 10);

    digits[4].showNumber(seconds / 10);

    digits[5].showNumber(seconds % 10);

    }

    public void play() {

    delayTimeline = new Timeline();

    delayTimeline.getKeyFrames().add(

    new KeyFrame(new Duration(1000 - (System.currentTimeMillis() % 1000)), new EventHandler<ActionEvent>() {

    @Override public void handle(ActionEvent event) {

    if (secondTimeline != null) {

    secondTimeline.stop();

    }

    secondTimeline = new Timeline();

    secondTimeline.setCycleCount(Timeline.INDEFINITE);

    secondTimeline.getKeyFrames().add(

    new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {

    @Override public void handle(ActionEvent event) {

    refreshClocks();

    }

    }));

    secondTimeline.play();

    }

    })

    );

    delayTimeline.play();

    }

    public void stop(){

    delayTimeline.stop();

    if (secondTimeline != null) {

    secondTimeline.stop();

    }

    }

    }

    public static final class Digit extends Parent {

    private static final boolean[][] DIGIT_COMBINATIONS = new boolean[][]{

    new boolean[]{true, false, true, true, true, true, true},

    new boolean[]{false, false, false, false, true, false, true},

    new boolean[]{true, true, true, false, true, true, false},

    new boolean[]{true, true, true, false, true, false, true},

    new boolean[]{false, true, false, true, true, false, true},

    new boolean[]{true, true, true, true, false, false, true},

    new boolean[]{true, true, true, true, false, true, true},

    new boolean[]{true, false, false, false, true, false, true},

    new boolean[]{true, true, true, true, true, true, true},

    new boolean[]{true, true, true, true, true, false, true}};

    private final Polygon[] polygons = new Polygon[]{

    new Polygon(2, 0, 52, 0, 42, 10, 12, 10),

    new Polygon(12, 49, 42, 49, 52, 54, 42, 59, 12f, 59f, 2f, 54f),

    new Polygon(12, 98, 42, 98, 52, 108, 2, 108),

    new Polygon(0, 2, 10, 12, 10, 47, 0, 52),

    new Polygon(44, 12, 54, 2, 54, 52, 44, 47),

    new Polygon(0, 56, 10, 61, 10, 96, 0, 106),

    new Polygon(44, 61, 54, 56, 54, 106, 44, 96)};

    private final Color onColor;

    private final Color offColor;

    private final Effect onEffect;

    private final Effect offEffect;

    public Digit(Color onColor, Color offColor, Effect onEffect, Effect offEffect) {

    this.onColor = onColor;

    this.offColor = offColor;

    this.onEffect = onEffect;

    this.offEffect = offEffect;

    getChildren().addAll(polygons);

    getTransforms().add(new Shear(-0.1,0));

    showNumber(0);

    }

    public void showNumber(Integer num) {

    if (num < 0 || num > 9) num = 0;

    for (int i = 0; i < 7; i++) {

    polygons[i].setFill(DIGIT_COMBINATIONS[num][i] ? onColor : offColor);

    polygons[i].setEffect(DIGIT_COMBINATIONS[num][i] ? onEffect : offEffect);

    }

    }

    }

    public static void main(String[] args) {

    launch(args);

    }

    }


  • Geertjan Wednesday, April 29, 2015

    You want me to explain in the comments to this random blog entry, which has nothing to do with your code, what that code does?


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.