Game-Apps für Smartphones und Tablets

Bern University oh Teacher Education  
HomeOnline-Editor startenDruckenAndroid-TurtlegrafikJava-Online

Hexa Pacman


Der HexaPacman kann mit Taps im linken bzw. rechten Teil des Displays gedreht werden. Er muss versuchen, möglichst viel Juwelen zu schnappen. Für jedes verschlungene Juwel steigt sein Lebensbalken an. Wird er dagegen von einem Juwel getroffen, verliert er an Leben.

Sinkt der Lebensbalken auf Null, kann das Spiel durch einen Tap neu gestartet werden.

 

Beispiel im Online-Editor bearbeiten

App installieren auf Smartphone oder Tablet

QR-Code

Programmcode downloaden (HexaPac.zip)


// HexaPacman.java

package app.hexapacman;

import java.util.LinkedList;

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

public class HexaPacman extends GameGrid implements GGActorCollisionListener
{
  private GGStatusBar status;
  private Pacman hexaPacman;
  private GGPanel p;
  private LinkedList<Jewel> availableJewels = new LinkedList<Jewel>();
  private LinkedList<Jewel> allJewels = new LinkedList<Jewel>();
  private HealthPointBar hpBar;
  private int score;
  private final int initialHealth = 50;

  public HexaPacman()
  {
    super(WHITE, windowZoom(700));
    status = addStatusBar(30);
  }

  public void main()
  {
    getBg().clear(WHITE);
    p = getPanel(-10, 10, 0.5);
    p.setAutoRefreshEnabled(false);
    hexaPacman = new Pacman(p);
    PointD hexagonSpawnPoint = new PointD(0, 0);
    addActorNoRefresh(hexaPacman, toLocation(p.toPixelPoint(hexagonSpawnPoint)));
    for (int = 1; i < 10; i++)
    {
      Jewel j = new Jewel(availableJewels);
      allJewels.add(j);
      //jewel is added to the jewels list in reset() when added to gamegrid
      addActorNoRefresh(j, new Location(-100-100)); //out of sight
      hexaPacman.addCollisionActor(j);
    }
    hexaPacman.addActorCollisionListener(this);
    setSimulationPeriod(30);
    setPaintOrder(HexaPacman.classJewel.class);
    addMultiTouchListener(hexaPacman, GGMultiTouch.press | GGMultiTouch.pointerPress | GGMultiTouch.release);
    hpBar = new HealthPointBar(p, initialHealth);
    status.setText("Tap the screen to the right/left to turn Hexa-Pacman!");
    doRun();
  }

  public void act()
  {
    if (hpBar.isGameOver())
    {
      doPause();
      showToast("Game Over, tap screen to reset");
      // ugly way to prevent immediate restart
      setTouchEnabled(false);
      delay(1000);
      setTouchEnabled(true);
    }
    launchJewel();
  }

  private void launchJewel()
  {
    if (!availableJewels.isEmpty() && Math.random() < 0.05)
    {
      GGVector = new GGVector(10, 10);
      v.rotate(Math.random() * Math.PI * 2);
      PointD spawnPoint = new PointD(v);
      Actor spawningJewel = availableJewels.poll();
      spawningJewel.setLocation(toLocation(p.toPixelPoint(spawnPoint)));
      spawningJewel.setDirection(spawningJewel.getLocation().getDirectionTo(hexaPacman.getLocation()));
      spawningJewel.setActEnabled(true);
    }
  }

  public void reset()
  {
    status.setText("Tap the screen to the right/left to turn Hexa-Pacman!");
    availableJewels.clear();
    availableJewels.addAll(allJewels);
    score = 0;
    hpBar.setHealth(initialHealth);
    doRun();
  }

  public int collide(Actor arg1, Actor collisionPartner)
  {
    Jewel jewel = (Jewel) collisionPartner;
    if (jewel.isExploding())
      return 0;

    if (headedTowardsHexagonsMouth(jewel))
    {
      hexaPacman.eat();
      hpBar.update(10);
      score++;
      status.setText("Number of jewels eaten: " + score);
      jewel.reset();
    }
    else
    {
      hpBar.update(-7);
      jewel.explode();
    }
    return 0;
  }

  private boolean headedTowardsHexagonsMouth(Jewel jewel)
  {
    double flyingDirection = hexaPacman.getLocation().getDirectionTo(jewel.getLocation());
    return Math.abs(hexaPacman.getDirection() - flyingDirection) < 30;
  }
}

// ------- class Pacman -----------
class Pacman extends Actor implements GGMultiTouchListener
{
  private GGPanel p;
  private int angle = 0;
  private final int PER_PERIOD_ANGLE = 10;
  private int openMouthCountdown = 0;

  public Pacman(GGPanel p)
  {
    super(true"hexagon_pacman"2);
    this.= p;
  }

  public void reset()
  {
    setCollisionCircle(new Point(0, 0), 100);
  }

  public void act()
  {
    turn(angle);
    if (openMouthCountdown > 0)
      openMouthCountdown--;
    else
      show(0);
  }

  public boolean multiTouchEvent(GGMultiTouch touch)
  {
    if (!gameGrid.isRunning())
      gameGrid.doReset();

    if (touch.getEvent() == GGMultiTouch.pointerPress
      || touch.getEvent() == GGMultiTouch.press)
    { // turn on rotating state
      PointD userPoint = p.toUserPoint(new Point(touch.getX(), touch.getY()));
      if (userPoint.x > 0) //touch on the right side of display
        angle = PER_PERIOD_ANGLE;
      else
        angle = -PER_PERIOD_ANGLE;
    } // deactivate rotation on release
    else
    {
      angle = 0;
    }
    return true;
  }

  public void eat()
  {
    show(1);
    openMouthCountdown = 4;
  }
}

// -------- class Jewel -------------------
class Jewel extends Actor
{
  private LinkedList<Jewel> jewels;
  private int resetCounter;

  public Jewel(LinkedList<Jewel> jewels)
  {
    super(true"jewel"5);
    this.jewels = jewels;
  }

  public void act()
  {
    //this is quite a bit hacky:
    if (resetCounter == 1)
      reset();
    if (isExploding())
      resetCounter--;
    else
      move();
  }

  public void reset()
  {
    resetCounter = 0;
    jewels.addLast(this);
    setCollisionCircle(new Point(0, 0), 15);
    setLocation(new Location(-100-100)); //out of sight;
    setActEnabled(false);
    show((int) (Math.random() * 4));
  }

  public void explode()
  {
    show(4);
    resetCounter = 6;
  }

  public boolean isExploding()
  {
    return resetCounter > 0;
  }
}

// ----------- class HealthPointBar ---------
class HealthPointBar
{
  private GGPanel p;
  private double length;
  private final double MIN = -9.9;
  private final double MAX = 9.9;
  private final double MAX_LENGTH = MAX - MIN;

   public HealthPointBar(GGPanel p, double initLengthPercent)
  {
    this.= p;
    setHealth(initLengthPercent);
  }

  public void update(double percent)
  {
    //clean old rectangle
    p.setPaintColor(Color.WHITE);
    p.rectangle(-9.9, -9.9, length, -9.2, true);

    p.setPaintColor(Color.GREEN);
    length += percent * MAX_LENGTH / 100;
    length = Math.min(length, MAX);
    length = Math.max(length, MIN);
    p.rectangle(-9.9, -9.9, length, -9.2, true);
  }

  public boolean isGameOver()
  {
    return length <= MIN;
  }

  public void setHealth(double percent)
  {
    length = MAX_LENGTH * percent / 100 + MIN;
    update(0);
  }
}