Aegidius
 Plüss   Aplulogo
     
 www.aplu.ch      Print Text 
© 2021, V10.4
 
  JDroidLib
 
 

The source code of all examples is included in the JDroidLib distribution.

Bringing the conceptual strength of Greenfoot to Android

Greenfoot is an environment for teaching Java programming with the focus on game development. Greenfoot is much appreciated by teachers and students for its simple concept and user-friendly implementation in educational institutions all over the world. No doubt, constructing computer games is motivating, but because in these modern times everyone is much concerned by using smartphones and tablets, developing games as smartphone or tablet app gives an extra motivation kick. Therefore the simple concept of Greenfoot has been ported to Android smartphones by the JDroidLib framework. With JDroidLib developing game apps for smartphones is simplified by an order of magnitude compared to basic Android API programming. Because Java on Android is compiled using the standard javac compiler, Android is 100 percent Java. So what makes the difference? Because apps have a completely different (and in many respects much simpler) user interface compared to desktop applications, all of the GUI classes that are part of the Java SE API (especially the Swing and AWT classes) are replaced. JDroidLib hides most of these differences by exposing almost the same classes and methods as the desktop based framework JGameGrid.

Of course the Android development environment is much more complicated than Greenfoot's BlueJ based IDE. It is a challenge to simplify the installation and use of the Android development tools as much to be used in a classroom. (We suggest to use the online editor/compiler at http://www.jdroid.ch, that is part of a rich e-learning site of PHBern, the Institute for Teacher Education at the University of Berne (To load your own sprite images when using the Online-Editor, copy the image files in any subdirectory on the SD card, e.g. folder 'sprites', and use a qualified filename in the Actor constructor like "sprites/image.png").

We take the Greenfoot Wombat scenario that is used in many introductory teaching sessions and port it to Android. As you will see the code is almost the same and, due to the large panoply of JDroidLib methods, even simplified.

To understand how much the Android code coincide with the Greenfoot code, you should have the Greenfoot version at hand. (To get it, download and install the Greenfoot distribution from here or just get the scenario code from here.) In both versions there are the same 4 classes: WombatWorld (the application class) and three classes derived from Actor: Wombat, Leaf and Rock. We proceed top-down and first show the application class:

// WombatWorld.java

package
 ch.aplu.wombats;

import
 ch.aplu.android.*;
import
 android.graphics.Color;

public
 class WombatWorld extends GameGrid implements GGTouchListener
{
  protected static GGStatusBar status;

  public WombatWorld()
  
{
    super(8, 8, cellZoom(60), BLACK, "cell"false);
    status = addStatusBar(20);
 
 
}

  
public void main()
  
{
    
addTouchListener(this, GGTouch.click);
    
populate();
    
doRun();
  
}

  
public void populate()
  
{
    Wombat w1 
= new Wombat();
    
addActor(w1, new Location(3, 3));

    Wombat w2 
= new Wombat();
    
addActor(w2, new Location(1, 7));

    Leaf l1 
= new Leaf();
    
addActor(l1, new Location(5, 3));

    Leaf l2 
= new Leaf();
    
addActor(l2, new Location(0, 2));

    Leaf l3 
= new Leaf();
    
addActor(l3, new Location(7, 5));

    Leaf l4 
= new Leaf();
    
addActor(l4, new Location(2, 6));

    Leaf l5 
= new Leaf();
    
addActor(l5, new Location(5, 0));

    Leaf l6 
= new Leaf();
    
addActor(l6, new Location(4, 7));
  
}

  
public boolean touchEvent(GGTouch touch)
  
{
    Location location 
= toLocationInGrid(touch.getX(), touch.getY());
    
addActor(new Rock(), location);
    
return true;
  
}
}


Remarks:

  • Like in Greenfoot there is no public static void main() method. In JDroidLib the entry point of the app is the overridden void main() method that is invoked automatically by a separate thread when the app starts. It is important to know that all initializing actions should be performed in main() and not in the constructor (that runs before main()), because the Android graphics system is not ready before main() runs. This is the conceptual drawback of hiding the Android specific code.

  • Even the image files can be reused, possibly adapted in size, so cell.jpg is now expanded to 500x500 pixels. The actor sprite images are unchanged. The size of the background image and the sprite pictures are adapted automatically to the current device resolution.

  • Touch events creates new rocks by a fingertip. Touching the screen gives a new feeling for conventional mouse and console oriented programmers. Moreover the smartphone touch screen provides a wonderful teaching vehicle to demonstrate the power of object oriented programming:
    "Just hit the screen and another object of the same class with exactly the same properties and behavior is born".

The Wombats are animated like in the Greenfoot scenario (we maintain the original comments):

// Wombat.java

package
 ch.aplu.wombats;

import
 ch.aplu.android.*;
import
 java.util.List;

/**
 * Wombat. A Wombat moves forward until it can't do so anymore, at
 * which point it turns left. This wombat can not move over rocks. 
 * If a wombat finds a leaf, it eats it.
 * 
 */
public
 class Wombat extends Actor
{
  
private int leavesEaten;

  
public Wombat()
  
{
    
super(true"wombat");  // Rotatable
    leavesEaten 
= 0;
  
}

  
/**
   * Do whatever the wombat likes to to just now.
   */
  
public void act()
  
{
    
if (foundLeaf())
      
eatLeaf();
    
else if (canMove())
    
{
      
move();
      
setVertMirror(getDirection() == 180);
    
}
    
else
      
turnRandom();
  
}

  
/**
   * Check whether there is a leaf in the same cell as we are.
   */
  
public boolean foundLeaf()
  
{
    Actor leaf 
= gameGrid.getOneActorAt(getLocation(), Leaf.class);
    
if (leaf != null)
      
return true;
    
else
      
return false;
  
}

  
/**
   * Eat a leaf.
   */
  
public void eatLeaf()
  
{
    Actor leaf 
= gameGrid.getOneActorAt(getLocation(), Leaf.class);
    
if (leaf != null)
    
{
      
// eat the leaf...
      leaf.
removeSelf();
      leavesEaten 
= leavesEaten + 1;
      WombatWorld.status.set
Text("#eaten leafs: " + leavesEaten);
    
}
  
}

  
/**
   * Test if we can move forward. Return true if we can, false otherwise.
   */
  
public boolean canMove()
  
{
    
if (!isMoveValid())
      
return false;
    
List rocks = gameGrid.getActorsAt(getNextMoveLocation(), Rock.class);
    
if (rocks.isEmpty())
      
return true;
    
else
      
return false;
  
}

  
/**
   * Turn in a random direction.
   */
  
public void turnRandom()
  
{
    
// get a random number between 0 and 3...
    
int turns = (int)(4 * Math.random());

    
// ...and turn left that many times.
    
for (int i = 0; i < turns; i++)
      
turnLeft();
  
}

  
/**
   * Turns towards the left.
   */
  
public void turnLeft()
  
{
    
turn(90);
  
}

  
/**
   * Tell how many leaves we have eaten.
   */
  
public int getLeavesEaten()
  
{
    
return leavesEaten;
  
}
}

Remarks:

  • You don't have to struggle with coordinates, the Actor's class move() method moves the actor to the adjacent grid cell in the current moving direction. isMoveValid() tests, if the next move will fall outside the grid. So the original canMove() is much simplified (which can be considered to be a didactical loss).

  • In JDroidLib an actor may have several sprite images, but at a given time only one of them is visible. Each image is identified by an integer sprite identifier. Sprite images are considered to be part of the actor and are defined when the actor is created. Changing the sprite image at run-time is performed by simply changing the visible sprite id. To flip an image horizontally or vertically setHorzMirror() or setVertMirror() is used.

  • The Actor's turn() method rotates the actor for the given rotation angle with respect to the current direction. This simplifies the code in many situations (which again can be considered to be a didactical loss).

 

Finally the Leaf and Rock classes are only necessary to refer to them in the Wombat class when checking for the presence of a leaf or rock object.

// Leaf.java

package
 ch.aplu.wombats;

import
 ch.aplu.android.*;

public
 class Leaf extends Actor
{
  
public Leaf()
  
{
    
super("leaf");
  
}
}

// Rock.java

package
 ch.aplu.wombats;

import
 ch.aplu.android.*;

public
 class Rock extends Actor
{
  
public Rock()
  
{
    
super("rock");
  
}
}
 

Remarks:

  • The Greenfoot's common navigation controls (Run/Pause, Act, Reset, Speed) can be mapped to the smartphone buttons by using a different GameGrid constructor.

  • WombatWorld is a full-fledged Android app that presents and behaves like a professional game. Try it on your smartphone. (To reset/restart is, change the smartphone orientation.)

  • Enjoy!

 

  wombat

 

Download Android app for installation on a smartphone or emulator.
Download sources (WombatWorld.zip).
Create QR code to download Android app to your smartphone.
Install/Start
app on a USB connected smartphone or a running emulator.

(This is a WebStart signed by the University of Berne, Switzerland. It installs some helper files in <userhome>.jdroidtools.If you did not install the Android SDK, you may install a slim version of the Android-Emulator in <userhome>.jdroidemul using this link, To start the emulator, execute ExecEmul.jar found in <userhome>.jdroidemul)