Objekte und Klassen


Processing 2.0

Das Programmieren mit Objekten und Klassen nennt man objektorientiertes Programmieren. Es bietet den Vorteil einer viel besseren Übersicht bei längeren Programmen und ist hier auch dringend anzuraten.

Grundlegendes:

Eine Klasse ist eine allgemeine Vorlage für ein Objekt. Nehmen wir als Beispiel ein Auto. Die Vorstellung von einem Auto wäre dann die Klasse. Die Eigenschaften eines Autos sind in der Klasse definiert (Bsp.: Auto hat 4 Räder, 4 Lichter, ein Lenkrad usw.). Manche Eigenschaften können aber auch variieren, so dass z.B. manche Autos unterschiedlich viele Sitze oder Türen aufweisen (oder auch eine aktuelle Geschwindigkeit und Richtung). Diese Eigenschaften übermittelt man der Klasse, wenn nach ihrer Vorlage ein Objekt erstellt werden soll. Ein Auto hat aber nicht nur Eigenschaften, die in Form von Variablen gespeichert werden, sondern auch Funktionen. Man nennt diese Methoden. Mit Hilfe dieser Methoden kann man ein Objekt manipulieren. Das Auto könnte beispielsweise beschleunigen und somit ändert sich die aktuelle Geschwindigkeit. Es könnte auch nach links lenken und somit die Fahrtrichtung ändern.

Beispiel: Auto starte Applet

Wir schreiben eine Klasse Auto. Diese Autos sollen beschleunigen, bremsen und lenken können.

Klasse: Auto

class Auto {
}

Die Richtung der Autos wird über einen Winkel gesteuert, der dann in einen Richtungsvektor umgerechnet wird. Er kann durch die Tasten LINKS, RECHTS verändert werden. Der Richtungsvektor wird dann jeden draw() Durchlauf zum aktuellen Positionsvektor addiert. Und schon bewegt sich das Auto. Durch die Tasten AUF, AB kann das Auto dann noch beschleunigen und bremsen.

Eigenschaften: Farbe, aktuelle Richtung,  aktuelle Position usw.

class Auto {
// 2 Vektoren für die Position und Richtung des Fahrzeugs
PVector position;
PVector direction;
// Richtung und Geschwindigkeit
float angle, speed;
//Farbe
color clr;
}

Methoden: Lenken, Beschleunigen, Bremsen, Auto zeichnen,  usw.

class Auto {
  //Methode des Objekts Auto
void render() {
fill(clr);
//der Code ab hier zeichnet des Autos
PVector normVector = new PVector(1, 0);
float angle = direction.heading2D();
pushMatrix();
translate(position.x, position.y);
rotate(angle-PI);
strokeWeight(2);
rect(0, 0, 100, 50);
fill(0);
rect(-35, -30, 15, 3);
rect(-35, 30, 15, 3);
rect(30, -30, 15, 3);
rect(30, 30, 15, 3);
popMatrix();
}

//Methode des Objekts Auto
void move() {
// aufgrund des Winkels wird die Bewegungsrichtung angepasst
direction.x = sin(angle)*speed;
direction.y = cos(angle)*speed;
position.add (direction);
}

void steerLeft() {
angle+=0.1;
}

void steerRight() {
angle-=0.1;
}

void accelerate() {
speed+=0.1;
}

void slowDown() {
speed-=0.1;
}
}

Der Konstruktor

Damit das Ganze dann auch wirklich funktioniert, brauchen wir noch einen sog. Konstruktor. Das ist eine Methode, die bei der Erzeugung des Objekts ein mal aufgerufen wird und die initialen Werte für das Objekt festlegt. Diese können beim Aufruf als Parameter mitgeliefert, oder einfach in der Klasse selbst festgelegt werden.

class Auto {
// der sog. Konstruktor wird einmal ausgeführt, wenn das Objekt erstellt wird
Auto (float aangle, color aclr) {
position    = new PVector (width/2, height/2);
angle = aangle;
speed = 0.1;
direction   = new PVector (0.1, 0);
clr    = aclr;
}
}

Die gesamte Klasse Auto sieht dann so aus:

class Auto {
// 2 Vektoren für die Position und Richtung des Fahrzeugs
PVector position;
PVector direction;
// Richtung und Geschwindigkeit
float angle, speed;
//Farbe
color clr;

// der sog. Konstruktor wird einmal ausgeführt, wenn das Objekt erstellt wird
Auto (float aangle, color aclr) {
position    = new PVector (width/2, height/2);
angle = aangle;
speed = 0.1;
direction   = new PVector (0.1, 0);
clr    = aclr;
}

//Methode des Objekts Auto
void render() {
fill(clr);
//der Code ab hier zeichnet des Autos
PVector normVector = new PVector(1, 0);
float angle = direction.heading2D();
pushMatrix();
translate(position.x, position.y);
rotate(angle-PI);
strokeWeight(2);
rect(0, 0, 100, 50);
fill(0);
rect(-35, -30, 15, 3);
rect(-35, 30, 15, 3);
rect(30, -30, 15, 3);
rect(30, 30, 15, 3);
popMatrix();
}

//Methode des Objekts Auto
void move() {
// aufgrund des Winkels wird die Bewegungsrichtung angepasst
direction.x = sin(angle)*speed;
direction.y = cos(angle)*speed;
position.add (direction);
}

void steerLeft() {
angle+=0.1;
}

void steerRight() {
angle-=0.1;
}

void accelerate() {
speed+=0.1;
}

void slowDown() {
speed-=0.1;
}
}

So, jetzt stellt sich noch die Frage, wie man Objekte erzeugt und manipuliert (also auf die Eigenschaften und Methoden zugreifen kann). Objekte verhalten sich diesbezüglich wie Variablen. Diese müssen, wie wir in Variablen gelernt haben, initialisiert und deklariert werden. Das gilt auch für Objekte.

Hier das Hauptprogramm des fertigen Beispiels:

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* KEYS
* UP               : Beschleunigen
* DOWN             : Bremsen
* LINKS            : Lenke nach links
* RECHTS           : Lenke nach rechts
*/

// Erzeugt ein Objekt der Klasse Auto
Auto Kaefer;

void setup() {
size(800, 800);
// Aufruf des Konstruktors
Kaefer = new Auto(0, color(100, 50, 255));
rectMode(CENTER);
}

void draw() {
background(200);
//Aufruf der Methoden des Objekts
Kaefer.move();
Kaefer.render();
}

// Zum Steuern des Autos werden
void keyPressed() {
if (key == CODED) {
if (keyCode == UP) {
Kaefer.accelerate();
}
else if (keyCode == DOWN) {
Kaefer.slowDown();
}
else if (keyCode == LEFT) {
Kaefer.steerLeft();
}
else if (keyCode == RIGHT) {
Kaefer.steerRight();
}
}
}

Und noch ein Beispiel: Wir wollen ein Programm schreiben, das Ameisen erzeugt, die sich auf zufälligen Bahnen bewegen und Futter sammeln. Dabei sollten ganz einfache Regeln gelten: Findet eine Ameise Futter und trägt gerade kein Futter, soll sie das gefunden Futter aufnehmen. Das trägt sie dann, bis sie wieder auf Futter stößt. Hier soll sie das Futter, das sie gerade trägt fallen lassen.

Wir werden also diesmal objektorientiert vorgehen und brauchen für unser Projekt 2 Objekte: das Futter und die Ameisen. Wie müssen diese Objekte nun beschrieben werden? Fangen wir der Einfachheit halber mit der Beschreibung des Futters an. Dieses hat in unserem Beispiel eine sehr passive Rolle und sollte somit leicht zu beschreiben sein.

Wie wir wissen nennt man die Beschreibung eines Objektes Klasse (class). Die sieht nun bei unserem Futter so aus:

class Food {
 float xPos;
 float yPos;
 float radius;
 int traeger;
 int frame=0;
 color clr;

 Food (float axPos, float ayPos, float aradius, int atraeger, color aclr) {
 xPos   = axPos;
 yPos   = ayPos;
 radius = aradius;
 traeger = atraeger;
 clr    = aclr;
 }

Dabei werden im oberen Teil die benötigten Variablen definiert. Unten haben wir den sog. Konstruktor. Dieser weist den Variablen xPos, yPos usw. von uns definierte Werte zu. Der Konstruktor gibt im Gegensatz zu einer Funktion nichts zurück, nicht einmal void und wird immer, wenn ein neues Objekt erstellt wird automatisch aufgerufen. Somit ist die Definition der Klasse abgeschlossen.

Um ein Objekt der Klasse Food zu generieren müssen wir noch folgendes machen:

1. Wir erzeugen eine Variable der Klasse Food mit dem Namen Futter.

Food Futter; </em>

2. Wir rufen eine Funktion mit dem Namen der Klasse auf und befüllen die Variable Futter mit einem Objekt der Klasse Food.

Futter=new Food();

3. Jetzt enthält unsere Variable Futter lauter Nullen für alle int, float und color – Variablen, die in ihr enthalten sind. Indem wir jetzt unseren Konstruktor aufrufen, befüllen wir unser Objekt mit den von uns gewünschten Werten. Dafür schreiben wir der besseren Übersichtlichkeit halber eine eigene Funktion.

void gibFutter() {
 Futter = new Food[50];
 int border = 50;
 for (int f=0; f<Futter.length; f++) {
 float xPos = random(border, width-border);
 float yPos = random(border, height-border);
 float radius = random(5, 5);
 int traeger = 500;
 color clr = color(50,200,0,120);
 Futter[f] = new Food(xPos, yPos, radius, traeger, clr);
 }
}

Diese Funktion nennen wir gibFutter. Sie erstellt in diesem Fall 20 Futterobjekte, die mit zufälligen Koordinaten versehen werden (und 50px Abstand zum Rand, damit sie auch von den Ameisen aufgenommen werden können).

Das wärs dann eigentlich. Fast das gleiche, aber mit ein wenig mehr Eigenschaften machen wir mit den Ameisen. Die Klasse inkl. dem Konstruktor sieht bei den Ameisen dann so aus:

class Animals {

 PVector position;
 PVector direction;
 float spin = 0.10;
 float radius;
 boolean loaded;
 int blocked;
 int traegt;
 color clr;

 Animals (float theX, float theY,
 float aradius, boolean aloaded, int ablocked, int atraegt, color aclr) {
 position    = new PVector (theX, theY);
 direction   = new PVector (10,10);
 direction.x = random (-1, 1);
 direction.y = random (-1, 1);
 radius = aradius;
 loaded = aloaded;
 blocked = ablocked;
 traegt = atraegt;
 clr    = aclr;
 }
}

Methoden:

Um unseren Objekten jetzt noch sagen zu können, was sie machen sollen, müssen wir in den Klassendefinitionen noch etwas ergänzen. Und zwar sog. Methoden. Das sind Funktionen in der Klassendefinition, die  Eigenschaften der Objekte ändern können. Z.B. sollten sich die Ameisen fortbewegen und am Bildschirm dargestellt werden können. Dafür haben wir 2 Methoden, nämlich die Methode render und die Methode move.

//Methode des Objekts Ameise
 void render() {
 fill(clr);
 //der Code ab hier dient dem Zeichnen der Ameise
 PVector normVector = new PVector(1,0);
 float angle = direction.heading2D();
 pushMatrix();
 translate(position.x,position.y);
 rotate(angle-PI);
 strokeWeight(2);
 line(0-radius/2,0-radius, 0+radius/2, 0+radius);
 line(0+radius/2,0-radius, 0-radius/2, 0+radius);
 line(0,0-radius, 0, 0+radius);
 ellipse(0,0,radius*0.5,radius*0.5);
 ellipse(0+radius-1,0,radius*1.5,radius);
 ellipse(0-radius*0.7,0,radius,radius);
 //ellipse(position.position.x, position.position.y, radius*2, radius*2);
 popMatrix();
 }

 //Methode des Objekts Ameise
 void move() {
 direction.x += random (-spin, spin);
 direction.y += random (-spin, spin);
 direction.normalize ();
 position.add (direction);

 if (position.x < radius || position.x > (width-radius)) {
 direction.x *= -1;
 }
 if (position.y < radius || position.y > (height-radius)) {
 direction.y *= -1;
 }
 }

Der Aufruf einer solchen Methode sieht dann so aus:

Ameise[i].move();
Ameise[i].render();

Außerdem können zu jedem Zeitpunkt alle Variablen eines Objekts abgerufen werden. Ein Beispiel für eine Abfrage kommt im gesamten Quellcode unten vor.

Unser Beispiel ist bewusst eher einfach gehalten und könnte noch um viele nette Features erweitert werden.

Beispiel Ameisen: starte Applet


Animals [] Ameise;
Food [] Futter;
//Der Aufruf einer solchen
//Methode sieht dann so aus:
void setup() {
 size(400, 400,P2D);
 gibFutter();
 buildAmeise();
 frameRate(120);
}

void draw() {
 background(180);
 //die erste Schleife geht in jedem Frame alle Ameisen durch
 for (int i=0; i<Ameise.length; i++) {
 //damit weichen die Ameisen einader aus
 for (int u=0; u<Ameise.length; u++) {
 if ((Ameise[i].position.x < Ameise[u].position.x+20 &&  Ameise[i].position.x > Ameise[u].position.x-20
 && Ameise[i].position.y < Ameise[u].position.y+20 && Ameise[i].position.y > Ameise[u].position.y-20) && u!=i) {
 Ameise[i].direction.x *= -1;
 Ameise[i].direction.y *= -1;
 }
 }

 Ameise[i].move();
 Ameise[i].render();
 Ameise[i].blocked-=1;

 //die zweite Schleife alle Futterpakete
 for (int f=0; f<Futter.length; f++) {
 if (Ameise[i].position.x < Futter[f].xPos+6 &&  Ameise[i].position.x > Futter[f].xPos-6
 && Ameise[i].position.y < Futter[f].yPos+6 && Ameise[i].position.y > Futter[f].yPos-6) {
 //hierhin kommt man nur, wenn eine Ameise auf Futter gestoßen ist
 if(Ameise[i].loaded && Ameise[i].traegt!=f) {
 //hier kommt hin, was passiert, wenn eine schon beladene Ameise auf Futter trifft
 Ameise[i].blocked=30; //Anzahl der Frames, bevor die Ameise wieder Futter aufnehmen kann
 Ameise[i].loaded = false;
 Futter[f].traeger=500;
 }
 else {
 //das passiert, wenn die Ameise auf Futter gestoßen ist, nichts trägt und nicht gesperrt ist.
 if (Ameise[i].blocked<1) {
 Ameise[i].traegt= f;
 Futter[f].traeger= i;

 Futter[f].dragged();
 Ameise[i].loaded = true;
 }
 }
 }
 Futter[f].render();
 println(Futter[f].traeger);
 }
 }
}

//diese Funktion erzeugt die Ameisen
void buildAmeise() {
 Ameise = new Animals[5];
 int border = 50;
 for( int i =0;i<Ameise.length; i++) {
 float X = random(border, width-border);
 float Y = random(border, height-border);

 float theX = random(border, width-border);
 float theY = random(border, height-border);

 float radius = random(15, 15);
 boolean loaded = false;
 int blocked = 0;
 int traegt = 50;
 color clr = color(120,100);
 Ameise[i] = new Animals(theX, theY, radius, loaded, blocked, traegt, clr);
 }
}

//hier beginnt die Definition des Objekts Ameise

void gibFutter() {
 Futter = new Food[50];
 int border = 50;
 for (int f=0; f<Futter.length; f++) {
 float xPos = random(border, width-border);
 float yPos = random(border, height-border);
 float radius = random(5, 5);
 int traeger = 500;
 color clr = color(50,200,0,120);
 Futter[f] = new Food(xPos, yPos, radius, traeger, clr);
 }
}

class Animals {

 PVector position;
 PVector direction;
 float spin = 0.10;
 float radius;
 boolean loaded;
 int blocked;
 int traegt;
 color clr;
 Animals (float theX, float theY,
 float aradius, boolean aloaded, int ablocked, int atraegt, color aclr) {
 position    = new PVector (theX, theY);
 direction   = new PVector (10,10);
 direction.x = random (-1, 1);
 direction.y = random (-1, 1);
 radius = aradius;
 loaded = aloaded;
 blocked = ablocked;
 traegt = atraegt;
 clr    = aclr;
 }

 //Methode des Objekts Ameise
 void render() {
 fill(clr);
 //der Code ab hier dient dem Zeichnen der Ameise
 PVector normVector = new PVector(1,0);
 float angle = direction.heading2D();
 pushMatrix();
 translate(position.x,position.y);
 rotate(angle-PI);
 strokeWeight(2);
 line(0-radius/2,0-radius, 0+radius/2, 0+radius);
 line(0+radius/2,0-radius, 0-radius/2, 0+radius);
 line(0,0-radius, 0, 0+radius);
 ellipse(0,0,radius*0.5,radius*0.5);
 ellipse(0+radius-1,0,radius*1.5,radius);
 ellipse(0-radius*0.7,0,radius,radius);
 //ellipse(position.position.x, position.position.y, radius*2, radius*2);
 popMatrix();
 }

 //Methode des Objekts Ameise
 void move() {
 direction.x += random (-spin, spin);
 direction.y += random (-spin, spin);
 direction.normalize ();
 position.add (direction);

 if (position.x < radius || position.x > (width-radius)) {
 direction.x *= -1;
 }
 if (position.y < radius || position.y > (height-radius)) {
 direction.y *= -1;
 }
 }
}
//hier beginnt die Definition des Objekts Futter
class Food {
 float xPos;
 float yPos;
 float radius;
 int traeger;
 int frame=0;
 color clr;

 Food (float axPos, float ayPos, float aradius, int atraeger, color aclr) {
 xPos   = axPos;
 yPos   = ayPos;
 radius = aradius;
 traeger = atraeger;
 clr    = aclr;
 }
 //Methode des Objekts Futter
 void render() {
 fill(clr);
 ellipse(xPos, yPos, radius*2, radius*2);
 }

//Methode des Objekts Futter
void dragged() {
if (Ameise[traeger].blocked<1) {

xPos=Ameise[traeger].position.x;
yPos=Ameise[traeger].position.y;
}

}
}

Aufgabe: Programmiere basierend auf dem Ameisen-Beispiel ein kleines Autorennspiel.

Advertisements

5 Kommentare

  1. Pingback: Processing – Über dieses Weblog « processing – tutorial

  2. Pingback: Variablen und Datentypen « processing – tutorial

  3. Pingback: Strings « processing – tutorial

  4. koberger

    Programm Race von Simon:

    int spielstatus =0;
    int x=110;
    int zeit =5;
    float farbe1=random(100,255);
    float farbe2=random(100,255);
    float farbe3=random(100,255);
    float punkte=0;
    int fehler=0;
    float time=0;
    int endpunkte=0;

    void setup(){
    size(500,500);
    PFont font = loadFont(„text.vlw“);
    textFont(font,20);
    noStroke();
    }

    void draw(){
    background(50);
    time=time+1;
    //startfeld
    if(spielstatus==0){
    text(„Drücken Sie eine beliebige Taste“,width/2-140,height/2);
    }
    //spielstart
    if(keyPressed){
    spielstatus=1;
    }

    if(spielstatus==1){
    /// gras
    fill(10,230,10);
    rectMode(CORNER);
    rect(0,0,100,height);
    rect(400,0,100,500);
    /////

    // spieler
    fill(farbe1,farbe2,farbe3);
    rectMode(CORNERS);
    rect(x,370,x+80,480);

    rectMode(CORNER);
    h1.update();
    h2.update();
    h3.update();
    h4.update();

    g1.update();
    g2.update();
    g3.update();

    punkte();
    crash();
    if(fehler<10){
    text("Fehler",420,160);
    text(fehler,440,200);
    }

    if(x400){
    punkte=punkte-30/frameRate;}

    }

    }

    void keyPressed(){
    if(keyPressed){
    if(key==CODED){
    if(keyCode==LEFT && x>50){
    x=x-100;
    }
    if(keyCode==RIGHT && x height+0) {
    ypos = -100;
    }
    //weiße linien
    fill(255);
    rect(198, ypos,4, 100);
    rect(298, ypos,4, 100);

    }
    }

    ////////////////// gegner ///////

    Gegner g1 = new Gegner(110,random(-200,0),random(2,6),zeit);
    Gegner g2 = new Gegner(210,random(-600,0),random(2,6),zeit);
    Gegner g3 = new Gegner(310,random(-400,0),random(2,6),zeit);

    class Gegner {
    float xpos,ypos,speed,timer;
    Gegner (float x,float y, float s,float t) {
    xpos=x;
    ypos = y;
    speed = s;
    timer=t;
    }

    void update() {
    ypos=ypos+speed;
    fill(10,150,200);
    rect(xpos,ypos,80,110);
    if(ypos>height){
    ypos=random(-height,-100);
    speed=random(3,7);

    }
    }
    }

    void punkte(){
    punkte=punkte+0.1666;

    fill(0);
    text(„Punkte“,420,70);
    text(round(punkte),440,100);

    }

    void crash(){
    if((x==110) && g1.ypos>260 && g1.ypos 60){
    fehler=fehler+1;
    time=0;
    }
    if((x==210) && g2.ypos>260 && g2.ypos 60){
    fehler=fehler+1;
    time=0;

    }
    if((x==310) && g3.ypos>260 && g3.ypos 60){
    fehler=fehler+1;
    time=0;

    }

    if(fehler>9){

    endpunkte=round(punkte);
    punkte=endpunkte;

    fill(0);
    rect(0,0,1000,1000);
    fill(255);
    text(„Du hast „+endpunkte+“ Punkte erreicht“,width/2-120,height/2);
    text(„Taste drücken zum Neustart“,width/2-120,height/2+50);
    if(keyPressed){
    fehler=0;
    punkte=0;
    spielstatus=1;
    }

    }
    }

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: