Tag 2 0. BreakOut Game

Einige der inzwischen kennengelernten Techniken wollen wir nun in einem kleinen Game zusammenfassen. Es handelt sich um eine Variante des Genres "Breakout", das schon seit den frühen Arcade-Zeiten immer wieder neue Varianten hervorbrachte: Breakout


// Breakout - Game V0.1
// Creative Coding 2017
// Future Features: 3 Leben, Ball wird schneller, Timer besser, Game mit Taste starten

float balkenWidth = 80;            // Breite des "Schiffs"
float balkenX = 100;               // Start-Position des "Schiffs"
float speedX = random(4,8);        // X-Geschwindigkeit des Balls
float speedY = random(-4,-8);      // Y-Geschwindigkeit des Balls
float size = 20;                   // Groesse des Balls
float posX, posY;                  // Variablen, um die Position des Balls in jedem Frame zu speichern
float time = 0;                    // Variable für Timer

boolean leftKey = false;
boolean rightKey = false;

void setup(){
   size(690,400); 
   posX = width/2;                 // starte den Ball in der Fenstermitte
   posY = height/2;
   rectMode(CENTER);
   fill(0);
}

void draw(){
  background(255);
      
  // Messe die Zeit und gebe sie links oben aus
      time = millis()/1000.;
      textSize(12);
      text(round(time), 10, 10);
  
  // Lass den Ball am Fensterrand abprallen
      if(posX > width-(size/2.) || posX < 0+(size/2.)){
       speedX = -speedX;
      }
      if(posY < 0+(size/2.)){
        speedY = -speedY;
      }
      
  // Aktualisiere die Ball-Position
      posX += speedX;
      posY += speedY;
  
  // Zeichne den Ball
      ellipse(posX, posY, size, size);
  
  // Aktualisiere die X-Position des Balkens, wenn er innerhalb des Fenster-Rands ist
      if (mouseX > 0+(balkenWidth/2) && mouseX < width-(balkenWidth/2)){
        //balkenX = mouseX;
      }
   if(leftKey == true && balkenX > 0 + (balkenWidth/2)){
       balkenX = balkenX - 10;
   }
   if(rightKey == true && balkenX < width - (balkenWidth/2)){
       balkenX = balkenX + 10;
   } 
      
  // Zeichne das "Schiff" 
      rect(balkenX, 380, balkenWidth, 20);
  
  // Abfrage, ob Ball "gefangen"
      if (posX > balkenX-(balkenWidth/2)-20 && posX < balkenX+(balkenWidth/2)+20){
        if (posY+(size/2) > 369 && posY+(size/2) < 380){
          speedY = -speedY;
          println("catch!");
        }
      }
  
  // Game Over!
      if(posY > height){
        textAlign(CENTER);
        textSize(50);
        text("YOU ARE DEAD!", width/2, height/2);
      }
  
}

void keyPressed(){
   if (key == 'a'){
         leftKey = true;
   }
   if(key == 's'){
         rightKey = true;
   }
   println(frameCount);
}

void keyReleased(){
   if (key == 'a'){
         leftKey = false;
   }
   if(key == 's'){
         rightKey = false;
   }
}




Bitmap-Grafiken hinzufügen

Um Bitmap-Bilder darzustellen existiert das PImage-Objekt. Wie etwa Ganzzahlen im Variablentyp int oder Fließkommazahlen im Typ float gespeichert werden, so werden Bilder im Variablentyp PImage gespeichert. Der Programmablauf zur Darstellung eines Bitmap-Bildes ist wie folgt:


PImage img;

void setup(){
   size(800,600);
   img = loadImage(„someBallPic.jpg“);
}

void draw(){
   background(0);
   image(img, 0, 0);
}

Aufgabe
Baue analog zum ›Ball‹ eine Grafik für das ›Schiff‹ ein.

Space-Background mit Klassen-Objekt

Siehe Kapitel Objektorientierte Programmierung.

Sound mit OSC und MAX

"Open Sound Control (OSC) is a protocol for communication among computers, sound synthesizers, and other multimedia devices that is optimized for modern networking technology. Bringing the benefits of modern networking technology to the world of electronic musical instruments, OSC's advantages include interoperability, accuracy, flexibility, and enhanced organization and documentation." (opensoundcontrol.org)

OSC-Messages bestehen in der Regel aus zwei Teilen – einem Schlüsselwort und einem Wert. Die Messages können jedoch nach Bedürfnissen und Zielen individuell definiert werden. Wichtig dabei ist nur, das Sender und Empfänger die gemeinsame Syntax verstehen.

oscP5 über "Library hinzufügen" herunterladen


import netP5.*;
import oscP5.*;

OscP5 osc;
NetAddress myRemoteLocation; 

void setup() {
  size(1280, 720);

  osc = new OscP5(this, 12000);					// 12000 ist die Port-Nummer, auf der auf einkommende Nachrichten gewartet wird
  myRemoteLocation = new NetAddress("127.0.0.1", 13000);  // Eine NetAddress (IP-Adresse, Port-Nummer) wird für ausgehende Nachrichten definiert. 
}

void draw() {
 
  // send OSC-Data to MAX
  OscMessage myMessage = new OscMessage("");
  myMessage.add("HI MAX!");
  osc.send(myMessage, myRemoteLocation);

}

Grundlegende, in MAX für das Beispiel benötigte Objekte:

Im Folgenden werden für die OSC-Kommunikation mit MAX die Messages "borderBounce", "shipBounce" und "death" definiert. Diese triggern die entsprechenden Sounds in MAX.

// Breakout - Game V0.2
// Creative Coding 2017
// Sounds mit MAX

import netP5.*;
import oscP5.*;

OscP5 osc;
NetAddress myRemoteLocation; 

float balkenWidth = 80;            // Breite des "Schiffs"
float balkenX = 100;               // Start-Position des "Schiffs"
float speedX = random(4,8);        // X-Geschwindigkeit des Balls
float speedY = random(-4,-8);      // Y-Geschwindigkeit des Balls
float size = 40;                   // Groesse des Balls
float posX, posY;                  // Variablen, um die Position des Balls in jedem Frame zu speichern
float time = 0;                    // Variable für Timer

boolean leftKey = false;
boolean rightKey = false;
boolean alive = true;

PImage ball;                        // deklariere PImage-Objekt 

void setup(){
   size(690,400);
   //fullScreen();
   posX = width/2;                 // starte den Ball in der Fenstermitte
   posY = height/2;
   rectMode(CENTER);
   ball = loadImage("Truncatedicosahedron.jpg");  // Lade Bilddatei aus data-Folder
   imageMode(CENTER);              // platziere Bild anhand seines Mittelpunkts
   fill(0);
   osc = new OscP5(this, 12000);          // 12000 ist die Port-Nummer, auf der auf einkommende Nachrichten gewartet wird
   myRemoteLocation = new NetAddress("127.0.0.1", 13000);  // Eine NetAddress (IP-Adresse, Port-Nummer) wird für ausgehende Nachrichten definiert. 
}

void draw(){
  background(255);
  if(alive == true){    
    // Messe die Zeit und gebe sie links oben aus
        time = millis()/1000.;
        textSize(12);
        text(round(time), 10, 10);
    
    // Lass den Ball am Fensterrand abprallen
        if(posX > width-(size/2.) || posX < 0+(size/2.)){
         speedX = -speedX;
         OscMessage myMessage = new OscMessage("");
         myMessage.add("borderBounce");
         osc.send(myMessage, myRemoteLocation);
        }
        if(posY < 0+(size/2.)){
          speedY = -speedY;
          OscMessage myMessage = new OscMessage("");
          myMessage.add("borderBounce");
          osc.send(myMessage, myRemoteLocation);
        }
        
    // Aktualisiere die Ball-Position
        posX += speedX;
        posY += speedY;
    
    // Zeichne den Ball
        ellipse(posX, posY, size, size);
        image(ball, posX, posY, size, size);
    
       
     // Tastensteuerung des "Schiffs"
     if(leftKey == true && balkenX > 0 + (balkenWidth/2)){        // wenn Taste a gedrückt wurde und die Position des Schiffs noch nicht am linken Rand anstösst
         balkenX = balkenX - 10;                                  // dann bewege das Schiff 10px nach links
     }
     if(rightKey == true && balkenX < width - (balkenWidth/2)){   // wenn Taste s gedrückt wurde und die Position des Schiffs noch nicht am rechten Rand anstösst
         balkenX = balkenX + 10;                                  // dann bewege das Schiff 10px nach rechts
     } 
        
    // Zeichne das "Schiff" an der zuvor berechneten Position
        rect(balkenX, 380, balkenWidth, 20);
    
    // Abfrage, ob Ball "gefangen"
        if (posX > balkenX-(balkenWidth/2)-20 && posX < balkenX+(balkenWidth/2)+20){
          if (posY+(size/2) > 369 && posY+(size/2) < 380){
            speedY = -speedY;
            OscMessage myMessage = new OscMessage("");
            myMessage.add("shipBounce");
            osc.send(myMessage, myRemoteLocation);
            println("catch!");
          }
        }
    
    // Game Over!
        if(posY > height){          
           alive = false; 
           OscMessage myMessage = new OscMessage("");
           myMessage.add("death");
           osc.send(myMessage, myRemoteLocation);
        }
  }else{
      textAlign(CENTER);
      textSize(50);
      text("YOU ARE DEAD!", width/2, height/2);
      
  }
}

void keyPressed(){
   if (key == 'a'){
         leftKey = true;
   }
   if(key == 's'){
         rightKey = true;
   }
   if(key == 'r'){
          posX = width/2;                 // starte den Ball in der Fenstermitte
          posY = height/2;
          alive = true;
   }
   println(frameCount);
}

void keyReleased(){
   if (key == 'a'){
         leftKey = false;
   }
   if(key == 's'){
         rightKey = false;
   }
}





----------begin_max5_patcher----------
1338.3oc2ZssjahCD8YOeET9YuSoqbIus6uv93VoRICx1jxHbAxSlIox7su5
FdryXChwfpM6KFjLf5yoa0mtw9GOrX455m4sKi9Tz+DsXwOdXwByT5IV3FuX
YE6478rVyksrh21x1xWtx9cR9yRy7sbYz55lBdyeWeTTz88sxW1yMWP2LhiU
kh8bo4wgdax5ixtYgtY2TKjBVk89+ylR19tGh8hkubfas8kKi9r6qJKLWd85
u9Gvjkm8jZK+t4pg3GAtoOvj46JEa+RCOWZeRwwIpuNh.Q5CPfYDRcKQeVeO
+7gGzerZBXqBNSt6+PjU73IKJBcAYglExpgyJhZ2Ud3wuwdZR3JOYD7xaC8T
Lv.VHUeH07olM9PHWv+lZAeGvWebyFdyqFrOl.E3UAO5Vfey9Zlb4J05wDau
NSPFlIvw.aPP5cQE2bGij0HCoyG1Cjg.nAx5Pc0Axb37Orm8xqefTpiz42Vt
UnSUzm2G4AUjjY791LA+168i6AwHRxizf37m488935AdPDNWO.LKtdS1+y1F
DXQf9h8c0J3z.fXzbJBL6YBFVFfLLWzoBff+uHO.g1Cjww3.oBL5REm9LADh
GLQWlfYpD.UQyiMk37VybBZ70L6JWxUybF9tJYVkKjuoto50SzAeuZkJqE40
6qarqH3QLDmfhWoNijkBvY5yRAIoDr9LvInM.YROsHxihR4sCzhcy1YdWXLf
TDlZVXDEjkZrJsAplBdlsjuiIp2rQsV5aDLXVqdNYeYq43a9PaR0St9qja8b
ube8Az0BTBUqEBSw1c.3GoYD.kdZE21TVboOghxHPM5wJmCkpOipjRhytjGV
u8haiBfYwlaKAlDSRLmoNBhc21TK8Zx8DXQWBbXF2o551.MShtych2g0bwwC
SEchtv36hKVeTJqE2EPuoa913CObElHpUa8zm+FAO3vxl+FCODY3VCmF3ciM
pV8NWww+kZiZN2Tif6Ty9WO4DxU4DRubxp2e7p7TZezTJvHcj3dGR2WaTaYk
hWm6pDKExqWOTuQCVgRacxPn60FblR2AViRPVxa9BWvVaMbvGfB3eufk+5cI
WAdO1nCiMJwT+Kg184TFoKqaeoZc894YG9Ucmz3gCaiO2cN0xvaZpqBOpgCi
5rIA040UUbg7J0ckyKeRk8hIYQZNH5PSctpHMksDUKhNT2HUcY..f6hVtRPN
run7XfUtxlfxFqi.Sb+tMkB4Tipr9.koWUHAMe8ver3PmG89cZi7sXObnLh1
Wnr4gpZfR7q+JglGnd9K4i15iM4cljS4KBdxzJ3sxRAS2f5YWitFvn2zA1UV
TvEm6KKJa0BBE2VQXLlCZHyANf4TUVbnVEm5nDrqzFKSpZlyPqH5YRaSK..C
..cUYiA.vLvY..BAyI.zuyjAQPBdbHHEmosYhpTyD0QJF91v4.Cd.A5H8APi
ISs8vc9nqa+coAz08ZxKDD.AGMfHNoRbxoQ9An6yCE6Afz+3FAJsC0G6gBCm
8.8vdRCl4PndXNDR3rGhO4nFoJgUWfn1RSOsA2LZFRPQ7w+hiCGg5y1wjfYN
XuRODN54WJ.551S.MGehdHzfYOHuBeBW1SepfIbIqfIyPAUwIwgrfJX7TfgI
zd7R..EN6wmDDv.FygldARWeGgQfD5SJNX.cvfomPQT2ek0fPndX+Yen1Rsu
noDW+CgoeHePCcTBx80di8krvNb3IdSqaQLl1xJ1Ws+zuIqLCKE1gXyvF9Sk
cWucFVS9tRIOWdrw9lkdN09NbWVUqVXwwRG2nV4e9v+h6+i2K
-----------end_max5_patcher-----------

Frei verwendbare Sound-Files im benötigten wav-Format finden sich unter anderem auf der Seite freesound.org
Sounds müssen in MAX im FileBrowser hinzugefügt werden.

Aufgabe
Erstelle einen Processing-Patch, der die Maus-Koordinaten per OSC zu MAX schickt. Dort sollen die Daten verwendet werden, um einen Sample-Loop in seiner Abspielgeschwindigkeit und seiner Lautstärke zu steuern. In MAX ist das Objekt groove~ für Sample-Loops gut geeignet.



----------begin_max5_patcher----------
888.3ocwWssbaBCD8Y7WAid1siDX7k7kzNYx3Q.xXkBHpjvwtYh+1qt.9RpA
SrwzWPiVc6bN6JsKuOxADx1RD.2mbe10w48QNNFSZCNU8c.Y3sQoXgYZfLhP
fSHfw1wjjsRi8TFqvEUaVH2kRL1qsjWlQySIRyt3czHqTVaEUYs.KiVSySVx
IQRK1PSW7c3XWOOecyDeamuCceoZMzXyowBe8an40mocuk6JH1cA.beQOxGi
Fo+LtiTVgxPBGb.cbbFQR3KI43PKKgci1nKRaulosWvjSnc.rUZGzLsG6BBw
4I2F8axiyI33Gk+1el0EOAoa7CZk398s+l7lZe+G9FVtZEgu2UGoKvYEoDvC
xsWwdejoAgZObG0H8Wkxvx6y4mfo46+eD5ilN2nASmYZLW+mFzfD.aTBDzjb
bpVCn4xaSBH+IFGs+thzgWkmSlZhzmDT+8R7bAn+hlUBy96J9Ec8mslYu81Z
3q+0bc84UXAIUAR2Bl3G5O+riBf+EE.+Vbpy7L2bmYdwFAaUAZNck4Z63is8
pVHYhcYgrzGUPPkFrvJAHTaRPvzg4I7UbV1fvZjWmnMZXncYbgBgD5FhKxGB
gOXxGf5.28FFpmvYrMjaHos+slvBEXY+hVSZ2gDVGd+qFv3Mj3kpUqNxkXoj
SCKk1h2cNHJJYBKnQxxbpNk+StSlTk7wArhklxdKIkEhSkjrB1Iolzixyv4x
HFWyIJK+rQ05GIO1BRSrMHSb.a1wERLW13LxXwV41.PPsYFmlPU7LkjmHWe5
pkznec5FTOyCP2dIqZzh0XAoskWPUtrFX2uKwoT4tK.OIUU9qjSTqsZysQm1
vy5fTyo.Ro4e9eoLwIZ6mG4JXk7nZOdUN.WzAOcr5HULsFlOer.e2iQSqoww
jSoAHiFWvTk4TgA8eMDnBAglJn7ldr2wChJz0wYbqvKdo6qP.30H.7qQ.zbj
AxH6aoA1rnm0y2VoAZ1L8LM8d.jS+N8UY27qPt9CN9cQq+hwJ1nCenooVN08
d.xIZdOPfdDNcROGP7zGWk5Q3DzA73ObvA1A3r3rmSuBbpyDyiI7ADevaEen
6Bedc.d5xwGH24mNpFvysd4ylWFWTrgvEU6oAJpZGekYTyYiMco41tlx9.bx
FZ87sVvbU8dRUoCkbaULama+mEScM77RZkqQcxeL5ufeF.99
-----------end_max5_patcher-----------

2-Spieler-Modus für PONG über WLAN

Wir haben die Library netP5 verwendet, um Daten zwischen zwei Anwendungen auszutauschen, die auf dem gleichen Computer ausgeführt werden. netP5 kann jedoch auch verwendet werden, um Daten über ein Netzwerk an andere Computer zu senden.
Um Daten an einen Computer im Netzwerk zu senden benötigen wir seine IP-Adresse. Diese ist bei OSX unter Systemeinstellungen/Netzwerk/Weitere Einstellungen/TCP-IP zu finden. Oder im Terminal mit dem Befehl ifconfig |grep inet

TCP/IP ist ein Übertragungsprotoll, das in Computernetzwerken (auch dem Internet) Anwendung findet. Die IP-Adresse ist die Adresse eines einzelnen Computers im Netzwerk.

Mit der Funktion oscEvent() können eingehende OSC-Nachrichten ausgewertet und in weiterer Folge zur Steuerung des Client-Programms verwendet werden:


/* incoming osc message are forwarded to the oscEvent method. */
void oscEvent(OscMessage theOscMessage) {
	println(theOscMessage);
	// what to do with it
}

Da nun die übertragenen Daten im Objekt OscMessage vorliegen kann überprüft werden, welche Messages gesendet wurden:


/* incoming osc message are forwarded to the oscEvent method. */
void oscEvent(OscMessage theOscMessage) {
	if(theOscMessage.checkAddrPattern("/leftPress")==true) {
    	
    }
}

Aufgabe
Schreibe einen Processing-Sketch, der die vom vorigen Sketch gesandten Mauskoordinaten empfängt. Steuere einen Kreis mit diesen Daten.

Aufgabe
Erstelle in Zweiergruppen eine Version von für zwei Spieler, die an zwei Computern über Netzwerk gegeneinander spielen.

Neben oscP5 und netP5 existiert die Network-Library von Processing selbst. Sie bietet Server- und Client-Objekte

Collsion Detection

collisionDetection Funktionen für Processing: https://github.com/jeffThompson/CollisionDetectionFunctionsForProcessing

Libraries, die neben anderen Funktionen Collision Detection beinhalten sind Box2D und ToxicLibs http://toxiclibs.org/2011/05/tutorials-galore/.