Sound (Android)

Einfache Einbindung von Sound/Video (und weitere GUI-Widgets) bietet die Library APWidgets (Achtung: manuelle Installation: entpacken in den Ordner "Processing/libraries").

Die Verwendung ist wenig überraschend: eine Variable für den Player, Befehle zum Abspielen, etc. (Und: Verwendung einer Variable zur Kontrolle, ob der Player an oder aus ist).

import apwidgets.*;

APMediaPlayer player;

int anaus;

void setup() {
  orientation(PORTRAIT);
  background(0);
  
  player = new APMediaPlayer(this);
  player.setMediaFile("Overworld_Theme.mp3");
  player.start();
  player.setLooping(true);
  player.setVolume(0.6, 0.6);
  
  anaus = 1;
}

void draw() {
}

void mousePressed() {
  if (anaus == 1) {
    player.pause();
    anaus = 0;
  } else if (anaus == 0) {
    player.start();
    anaus = 1;
  }
}

Location

Einbindung der Android-Ortsfunktionen wieder über Ketai. KetaiLocation liefert Ortsdaten über einen Callback: onLocationEvent. Die Daten kommen als Geographische Koordinaten (Längengrad/longitude, Breitengrad/latitude, Höhe über Meeresspiegel/altitude).

Speichern der Positionsdaten in drei Variablen vom Typ double.

import ketai.sensors.*; 

KetaiLocation location;

double lat, lng, alt;
PFont f;

void setup() {
  orientation(LANDSCAPE);
  f = createFont("HypatiaSansPro-Regular.otf", 32);
}

Abfragen der Ortsdaten über den Callback onLocationEvent. Hier z.B. die Daten in den dafür vorgesehenen Variablen abspeichern.

void onLocationEvent(double _latitude, double _longitude, double _altitude) {
  lat = _latitude;
  lng = _longitude;
  alt = _altitude;
  println("lat/lon/alt: " + lat + "/" + lng + "/" + alt);
}

Anzeigen der Daten in draw (dabei wichtig: "\n" in einem String erzeugt eine neue Zeile).

void draw() {
 background(0);
 textFont(f,32);
 textAlign(CENTER, CENTER);
 text("letzte Position:"+lat+"\n"+lng+"\nHöhe: "+alt,width/2,height/2);
}

Wieder einmal benötigt Ketai noch eine weitere Methode, damit alles funktioniert.

void onResume() {
  location = new KetaiLocation(this);
  super.onResume();
}

Zum ausprobieren: Mapping.zip

Verhalten und Zeit: Zustandsmaschinen

draw() legt fest, was von Frame zu Frame passiert. Damit das manchmal so und manchmal so passieren kann, muss in draw() entschieden werden, was zu tun ist (z.B. Startbilschirm zeigen, Spiel zeigen). Die Entscheidung trifft ein if. Was zu tun ist hängt vom Zustand des laufenden Programms ab: ist es am Anfang, am Ende, mitten drin...? Eine Zustandsvariable speichert daher, was gerade zu tun ist (dies ist ein einfacher "endlicher Automat/finite state machine"). Welche Zustände es gibt, wird über Variablen festgelegt, die den Zuständen Namen geben.

Zustände und Namen:

int STARTBILDSCHIRM = 0;
int ANIMATION = 1;

Speichern, welcher Zustand gerade aktiv ist:

int state;

void setup() {
  size(400,300);
  state = STARTBILDSCHIRM;
}

Wir unterscheiden draw-Varianten für die Zustände:

void drawStartbildschirm() {
  background(255);
  noStroke();
  fill(128,0,0);
  rect(10,10,width-20,height-20);
}

void drawAnimation() {
  background(255);
  noStroke();
  fill(0,128,0);
  float rectsize = random(100);
  rect(random(width),random(height),rectsize,rectsize);
}

Die eigentliche draw()-Methode trifft jetzt nur noch die Entscheidung was zu tun ist:

void draw() {
  if (state == STARTBILDSCHIRM) {
    drawStartbildschirm();
  } else if (state == ANIMATION) {
    drawAnimation();
  }
}

Und schließlich müssen die Zustände noch ineinander umschalten können. Z.B. mit der Maus.

void mousePressed() {
  if (state == STARTBILDSCHIRM) {
    state = ANIMATION;
  } else if (state == ANIMATION) {
    state = STARTBILDSCHIRM;
  }
}
Beispiel: Das Animations-Beispiel plus Zustandsmaschine.

Der Tab mit der Animationsklasse bleibt wie gehabt.
class InteractiveAnimation {
  ArrayList<PImage> images;
  int currentImage;
  float x;
  float y;
  InteractiveAnimation(String name, int imageCount) {
    images = new ArrayList();
    for (int i=1; i<=imageCount; i++) {
      images.add(loadImage(name+nf(i, 2)+".png"));
    }
    x = width/2;
    y = height/2;
  }
  void display() {
    imageMode(CENTER);
    image(images.get(currentImage), x, y);
    currentImage++;
    if (currentImage==images.size()) currentImage = 0;
  }
  void update() {
    if (dist(mouseX, mouseY, x, y) < 50) {
      x = x + random(-2, 2);
      y = y + random(-2, 2);
    }
  }
}

Der Rest wird um Zustände usw. ergänzt.

InteractiveAnimation ia;
PImage bg;

int START = 0;
int ANIMATION = 1;
int ENDE = 2;

PFont f;

int state;

void setup() {
  size(800,480);
  ia = new InteractiveAnimation("luigi",35);
  bg = loadImage("back.gif");
  f = createFont("HypatiaSansPro-Regular.otf",32);
  state = START;
}

void draw() {
  if (state == START) {
    drawStart();
  } else if (state == ANIMATION) {
    drawAnimation();
  } else if (state == ENDE) {
    drawEnde();
  }
}

void mousePressed() {
  if (state == START) {
    state = ANIMATION;
  } else if (state == ANIMATION) {
    state = ENDE;
  } else if (state == ENDE) {
    state = START;
  }
}

void drawAnimation() {
  imageMode(CENTER);
  image(bg,width/2,height/2);
  ia.update();
  ia.display();
}

void drawStart() {
  background(255);
  textAlign(CENTER,CENTER);
  textFont(f,32);
  text("Get ready for Luigi...",width/2,height/2);
}

void drawEnde() {
  background(255);
  textAlign(CENTER,CENTER);
  textFont(f,32);
  text("Ciao!",width/2,height/2);
}

Daten speichern (plus: Texteingabe/Button)

Die App merkt sich was passiert ist in einer Datei. Dazu Texteingabe und Button via APWidgets. APWidgets setzt alle Widgets (hier: APEditText und APButton) in einen Container (APWidgetContainer). Hinzufügen mit addWidget(...).

import apwidgets.*;

APWidgetContainer c;
APEditText textInput;
APButton saveButton;
String eingegebenerText = "Nichts...";

void setup() {
  c = new APWidgetContainer(this);
  saveButton = new APButton(20,height/2,200,60,"Speichern!");
  textInput = new APEditText(20,height/2+80,width-40,height/2-80);
  c.addWidget(button);
  c.addWidget(texting);
}

In draw() kann ganz normal gezeichnet werden. Die Widgets im Container werden von selbst dargestellt!

void draw() {
  background(0);
  text(eingegebenerText,20,20);
}

Der Callback onClickWidget(APWidget widget) wird aufgerufen, wenn ein Widget im Container angeklickt wurde. Mit if lässt sich überprüfen, ob das Widget unser button (hier mit Namen der Variable saveButton) war. Dann wird der Text aus dem Eingabefeld genommen und gespeichert.

Speichern mit saveStrings. Achtung: die Mathode arbeitet mit normalen Arrays, daher auf die [ und ] achten! Hier gilt: Das Array output enthält einen String (output[0]) und jeder String wird von saveStrings in eine Zeile einer Datei geschrieben. Speicherort der Datei ist die SD-Karte ("//sdcard/").

Nach dem Speichern versteck hide() den Container c.

void onClickWidget(APWidget widget){  
  if(widget == saveButton) {
    eingegebenerText = textInput.getText();
    String[] output = new String[1];
    output[0] = eingegebenerText;
    saveStrings("//sdcard/daten.txt", output);
    c.hide();
  }
}

Die Datei "daten.txt" liegt jetzt auf der Speicherkarte. Anschauen: Telefon mit dem Rechner verbinden und USB-Speicher aktivieren.

Daten laden

Erweiterung des letzten Beispiels um zwei Zeilen, die in setup() dafür sorgen, dass die Variable für den eigegebenen Text mit der ersten Zeile aus der Datei auf der SD-Karte gefüllt wird. Wegen der Array-Syntax heißt es dabei String[] inputs und eingegebener Text = inputs[0];.

import apwidgets.*;

APWidgetContainer c;
APEditText texting;
APButton button;
String eingegebenerText = "Nichts...";

void setup() {
  c = new APWidgetContainer(this);
  button = new APButton(20,height/2,200,60,"Speichern!");
  texting = new APEditText(20,height/2+80,width-40,height/2-100);
  c.addWidget(button);
  c.addWidget(texting);
  
  String[] inputs = loadStrings("//sdcard/daten.txt");
  eingegebenerText = inputs[0];
}

void draw() {
  background(0);
  text(eingegebenerText,20,20);
}

void onClickWidget(APWidget widget){  
  if(widget == button) {
    eingegebenerText = texting.getText();
    String[] output = new String[1];
    output[0] = eingegebenerText;
    saveStrings("//sdcard/daten.txt", output);
    c.hide();
  }
}