Game-Apps für Smartphones und Tablets

Pädagogische Hochschule Bern  
HomeOnline-Editor startenDruckenAndroid-TurtlegrafikJava-Online

Lunar Lander (Simulation Mondlandung)



Das Programm simuliert eine Mondlandung. Die Geschwindigkeit, Beschleunigung und die verbliebene Treibstoffmenge wird laufend berechnet. Es steht nur eine beschränkte Menge Treibstoff zur Verfügung.

Raketenschub (Massenausstoss pro Sekunde) kann mit der Volumentaste gesteuert werden:
Volume+: mehr Schub: der Fall wird gebremst
Volume-: weniger Schub

Ziel: Landung mit einer möglichst kleinen Geschwindigkeit. Ist die Geschwindigkeit grösser als 10 m/s kracht das Raumschiff in den Mond.


Beispiel im Online-Editor bearbeiten

 

App installieren auf Smartphone oder Tablet

QR-Code

Programmcode downloaden (LunarLander.zip)

   

 

Programmcode:
// LunarLander.java

package app.lunarlander;

import android.graphics.*;
import ch.aplu.android.*;
import ch.aplu.util.HiResTimer;
import ch.aplu.util.*;

public class LunarLander extends GameGrid
{
  public LunarLander()
  {
    super("moon"falsetruewindowZoom(300));
    setScreenOrientation(PORTRAIT);
  }
  
  public void main()
  {
    int screenWidth = getNbHorzCells();
    int screenHeight = getNbVertCells();
    setSimulationPeriod(50);
    Lander lander = new Lander(screenWidth, screenHeight);
    addNavigationListener(lander);
    addActor(lander, new Location(* screenWidth / 3, screenWidth / 7));
    doRun();
  }
}

class Lander extends Actor implements GGNavigationListener
{
  private int screenWidth;
  private int screenHeight;
  private int imageWidth;
  private int imageHeight;
  private final double yTouchdown = 350;
  private final double xmax = 300;
  private final double startFuel = 1000;  // Amount of fuel at start
  private final double fuelFactor = 0.5; // Fuel consumtion per simulation period and thrust level
  private final double amax = 1.6; // Free acceleration on moon
  private double x;  // Position in east direction
  private double y;  // Position in downward direction
  private double v;  // Speed in downward direction
  private double a;  // Accelleration in downward direction
  private int z; // Thrust level
  private double fuel; // Remaining fuel
  private Actor debris = new Actor("landerdebris");
  private Actor thrust = new Actor("thrust"9);
  private boolean isLanded = false;
  private HiResTimer timer = new HiResTimer();
  private final int xLeft = 10;
  private GGTextField status;
  private Location statusLoc;
  private GGTextField title;
  private Location titleLoc;
  private GGTextField fuelIndicator;
  private Location fuelIndicatorLoc;
  private GGTextField thrustIndicator;
  private Location thrustIndicatorLoc;
  private Location[] resultLocs = new Location[6];
  private GGTextField[] results = new GGTextField[resultLocs.length];
  private String[] fuelLevel = new String[12];

  public Lander(int screenWidth, int screenHeight)
  {
    super("lander");
    imageWidth = screenWidth;
    imageHeight = * imageWidth / 3;
    this.screenWidth = screenWidth;
    this.screenHeight = screenHeight;
    init();
  }
  
  // User coordinates 0..xmax -> screen ulx...screen urx
  private int toPixel(double user)
  {
    return (int)(screenWidth / xmax * user);
  }

  private double toUser(int pixel)
  {
    return xmax / screenWidth * pixel;
  }

  private void init()
  {
    statusLoc = new Location(xLeft, screenHeight - 10);
    status = new GGTextField(""statusLoc, false);
    status.show();
    titleLoc = new Location(10, 27);
    title = new GGTextField("LunarLander (www.aplu.ch). Thrust: Volume+-"titleLoc, false);
    title.setFontSize(20);
    title.show();
    fuelIndicatorLoc = new Location(xLeft, imageHeight / - 40);
    fuelIndicator = new GGTextField("Fuel level: 11"fuelIndicatorLoc, false);
    fuelIndicator.setTextColor(Color.YELLOW);
    fuelIndicator.setFontSize(30);
    fuelIndicator.show();
    thrustIndicatorLoc = new Location(xLeft, imageHeight / - 10);
    thrustIndicator = new GGTextField("Fuel level: 11"thrustIndicatorLoc, false);
    thrustIndicator.setTextColor(Color.YELLOW);
    thrustIndicator.setFontSize(30);
    thrustIndicator.show();
    for (int = 0; i < results.length; i++)
    {
      resultLocs[i] = new Location(xLeft, imageHeight / + 20 + 20 * i);
      results[i] = new GGTextField(""resultLocs[i], false);
      results[i].show();
    }
    for (int = 0; i < fuelLevel.length; i++)
      fuelLevel[i] = "fuel" + i;
  }

  public void reset()
  {
    GameGrid gg = gameGrid;
    setDirection(Location.SOUTH);
    x = toUser(getLocationStart().x);
    = toUser(getLocationStart().y);
    L.i("xStart = " + x);
    L.i("yStart = " + y);
    v = 0;
    a = amax;
    fuel = startFuel;
    show();
    setActEnabled(true);
    if (debris.gameGrid == null)  // not yet added to GameGrid
      gg.addActor(debris, new Location());
    debris.hide();
    if (thrust.gameGrid == null) // not yet added to GameGrid
      gg.addActor(thrust, new Location());
    thrust.hide();
    isLanded = false;
    gg.getBg().clear(Color.BLACK);
    for (int = 0; i < results.length; i++)
      results[i].setText("");
    timer.start();
    showFuelLevel(11);
    showThrustLevel(0);
  }

  public void act()
  {
    GameGrid gg = gameGrid;
    double vdisp = (int)(100 * v/ 100.0;
    double = yTouchdown - y;
    String = String.format("h=%4.1f m    v=%4.2f m/s"h, v);
    if (getNbCycles() % 10 == 0)
      status.setText(s);
    double dt = * gg.getSimulationPeriod() / 1000.0;  // Time scaled: * 2
    = + * dt;
    y = + * dt;
    setLocation(new Location(toPixel(x), toPixel(y)));
    thrust.setLocation(new Location(toPixel(x), 
      toPixel(y) + (int)(27 * gg.getZoomFactor())));
    fuel = fuel - * fuelFactor;
    if (fuel < 0)
      fuel = 0;
    for (int = 2; i <= 10; i++)
    {
      if (fuel <= (i - 1) * 100.0 && fuel > (i - 2) * 100.0)
      {
        if (fuel > 50)
          showFuelLevel(i);
      }
    }

    if (fuel <= 50 && fuel > 0)
      showFuelLevel(1);

    if (fuel == 0)
    {
      showFuelLevel(0);
      z = 0;
      a = amax;
      showThrustLevel(0);
    }

    if (h <= && !isLanded)
    {
      status.setText("Touchdown!");
      if (v > 10.0)
      {
        debris.setLocation(new Location(getLocation().x, getLocation().+ 30));
        debris.show();
        hide();
        results[0].setText("Crashed!");
        results[1].setText("Speed: " + vdisp + "m/s");
      }
      else
      {
        long time = timer.getTime();
        results[0].setText("Successfully landed!");
        results[1].setText("Speed: " + vdisp + "m/s");
        results[2].setText("Time used: " + time / 1000000 + "s");
        results[3].setText("Remaining fuel: " + (int)fuel + "kg");
      }
      results[4].setText("Play: 'Back' button");
      results[5].setText("Quit: 'Home' button");
      setActEnabled(false);
      z = 0;
      showThrustLevel(0);
      isLanded = true;
    }
  }

  public void navigationEvent(GGNavigationEvent event)
  {
    if (!gameGrid.isRunning())
      return;

    if (event == GGNavigationEvent.BACK_DOWN && isLanded)
    {
      reset();
      gameGrid.doRun();
      return;
    }

    if (fuel == 0)
      return;

    double da = 0.4;

    switch (event)
    {
      case VOLUME_INCREASE_REPEAT:
        if (z < 8)
        {
          a -= da;
          z++;
        }
        break;
      case VOLUME_DECREASE_REPEAT:
        if (z > 0)
        {
          a += da;
          z -= 1;
        }
        else
          a = amax;
        break;
    }
    showThrustLevel(z);
  }

  private void showFuelLevel(int i)
  {
    fuelIndicator.setText("Fuel level: " + i);
  }

  private void showThrustLevel(int z)
  {
    thrust.show(z);
    thrustIndicator.setText("Thrust: " + z);
  }
}