Entwicklung einer Rassel-App mit Processing
(EN google-translate)
(PL google-translate)
Idee: Unter Verwendung bisheriger Entwicklungen aus Simulationstchnik und OOP soll zunächst ein Programm in Processing geschrieben werden, das kleine Kreise animiert und Geräusche produziert, wenn diese kollidieren.
Die Kreise sollen tatsächlich wie Massepunkte simuliert werden und die Kollisionen sollen wie als lineare Feder-Dämpfer-Systeme realisiert werden.
Auch ein passendes Icon soll später hinzugefügt werden.
//import ketai.sensors.*; //für Android-Mode import java.util.Random; public int KUGELBREITE; public int KUGELANZAHL; public double dHALBKUGEL; public double dKUGELBREITE; public int[][] farbe; public int[] ton; public int[] pentaton = {62,64,67,69,71, 74,76,79,81,83,86,88}; public boolean[] kollision; public boolean[] kollision_alt; public int[][] ikoord; public double[][] koord; public double[][] vkoord; public double[][] akoord; public double dt=0.01; public double ax; //Beschleunigungsvektor in x- und y- Richtung public double ay; public double aphi; //Zufallsrichtung der Beschleunigung im Java-Mode public double C; //Federkonstante public double D; //Dämpfungskonstante public Random zufall; public Tonspieler tonspieler; public void setup() { size(500,500); //im Android-Mode entfernen //Gemäß den Abmessungen des Bildschrims die ganze Anwendung ausrichten: KUGELBREITE = width/20; if(height<width) KUGELBREITE = height/20; dHALBKUGEL=KUGELBREITE/2; dKUGELBREITE = (double)KUGELBREITE; int maxwert = (int)(width/KUGELBREITE) * (int)(height/KUGELBREITE); KUGELANZAHL = (maxwert*10)/100; zufall = new Random(System.currentTimeMillis()); farbe = new int[KUGELANZAHL][3]; ton = new int[KUGELANZAHL]; kollision = new boolean[KUGELANZAHL]; kollision_alt = new boolean[KUGELANZAHL]; for(int i=0;i<farbe.length;i++) { double alfa = 2.0*Math.PI*(double)i/(double)(farbe.length-1); double beta = 3.0*Math.PI*(double)i/(double)(farbe.length-1); double gamma = 5.0*Math.PI*(double)i/(double)(farbe.length-1); farbe[i][0] = 40+(int)(100.0*(1.0+Math.sin(alfa))); farbe[i][1] = 40+(int)(100.0*(1.0+Math.cos(alfa))); farbe[i][2] = 40+(int)(100.0*(1.0+Math.sin(alfa))); } for(int i=0;i<ton.length;i++) { ton[i] = pentaton[i%pentaton.length]; kollision[i]=false; kollision_alt[i]=false; } koord = new double[KUGELANZAHL][2]; ikoord = new int[KUGELANZAHL][2]; akoord = new double[KUGELANZAHL][2]; vkoord = new double[KUGELANZAHL][2]; //Initialisierungen aphi = 0.0; setBeschleunigung(); C=100.0; D=0.1; for(int i=0;i<KUGELANZAHL;i++) { int x = 0; int y = 0; boolean schonda=false; do { schonda=false; x = KUGELBREITE/2+KUGELBREITE * zufall.nextInt(width/KUGELBREITE); y = KUGELBREITE/2+KUGELBREITE * zufall.nextInt(height/KUGELBREITE); for(int k=0;k<i;k++) if(x==ikoord[k][0] && y==ikoord[k][1]) schonda=true; } while(schonda==true); ikoord[i][0]=x; ikoord[i][1]=y; koord[i][0]=(double)x; koord[i][1]=(double)y; } tonspieler = new Tonspieler(); tonspieler.start(); (new Thread((new Simulation()))).start(); //(new KetaiSensor(this)).start(); //erst im Android-Mode aktivieren frameRate(25); // im Android-Mode entfernen } public void draw() { clear(); background(255); noStroke(); for(int i=0;i<KUGELANZAHL;i++) { fill(farbe[i][0],farbe[i][1],farbe[i][2]); //ellipse(ikoord[i][0]+KUGELBREITE/2,ikoord[i][1]+KUGELBREITE/2,KUGELBREITE,KUGELBREITE); ellipse(ikoord[i][0],ikoord[i][1],KUGELBREITE,KUGELBREITE); } } void setBeschleunigung() //nur für Java-Mode, Zufallsrichtung der Beschleunigung { if(zufall.nextBoolean()) aphi+=0.1; else aphi-=0.1; ax=9.81*Math.cos(aphi); ay=9.81*Math.sin(aphi); } void euler() { setBeschleunigung(); for(int i=0;i<KUGELANZAHL;i++) { //Erdbescleunigung akoord[i][0] = ax; akoord[i][1] = ay; //linke Wand if(koord[i][0]<dHALBKUGEL) akoord[i][0]+= C*(dHALBKUGEL-koord[i][0]); //rechte Wand if(koord[i][0]>width-dHALBKUGEL) akoord[i][0]+= C*((width-dHALBKUGEL)-koord[i][0]); //obere Wand if(koord[i][1]<dHALBKUGEL) akoord[i][1]+= C*(dHALBKUGEL-koord[i][1]); //untere Wand if(koord[i][1]>height-dHALBKUGEL) akoord[i][1]+= C*((height-dHALBKUGEL)-koord[i][1]); //Kollisionsdetektion kollision[i]=false; for(int k=0;k<KUGELANZAHL;k++) { if(i!=k) { double dx = koord[i][0]-koord[k][0]; double dy = koord[i][1]-koord[k][1]; if(dx*dx+dy*dy<dKUGELBREITE*dKUGELBREITE) { double laenge = Math.sqrt(dx*dx+dy*dy); double kraft = dKUGELBREITE - laenge; if(laenge>0.01) { akoord[i][0]+=C*(kraft*dx/laenge); akoord[i][1]+=C*(kraft*dy/laenge); kollision[i]=true; } } } } if(kollision[i]==true && kollision_alt[i]==false) { tonspieler.spieleTon(ton[i]); } kollision_alt[i] = kollision[i]; //allgemeine Dämpfung akoord[i][0]-=D*vkoord[i][0]; akoord[i][1]-=D*vkoord[i][1]; vkoord[i][0]+=akoord[i][0]*dt; vkoord[i][1]+=akoord[i][1]*dt; koord[i][0]+=vkoord[i][0]*dt; koord[i][1]+=vkoord[i][1]*dt; ikoord[i][0] = (int)(koord[i][0]+0.5); ikoord[i][1] = (int)(koord[i][1]+0.5); } } void onAccelerometerEvent(float x, float y, float z) { ax=x; ax=y; } private class Simulation implements Runnable { public void run() { while(true) { euler(); try { Thread.sleep(10); } catch(Exception e) { } } } }
Code 0-1: Rassel-App im Java-Mode, kugel1.
package processing.test.kugel2; import processing.core.*; import processing.data.*; import processing.event.*; import processing.opengl.*; import ketai.sensors.*; import java.util.Random; //import javax.sound.midi.MidiSystem; //import javax.sound.midi.Synthesizer; //import javax.sound.midi.MidiChannel; import java.util.HashMap; import java.util.ArrayList; import java.io.File; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; public class kugel2 extends PApplet { //f\u00fcr Android-Mode public int KUGELBREITE; public int KUGELANZAHL; public double dHALBKUGEL; public double dKUGELBREITE; public int[][] farbe; public int[] ton; public int[] pentaton = {62,64,67,69,71, 74,76,79,81,83,86,88}; public boolean[] kollision; public boolean[] kollision_alt; public int[][] ikoord; public double[][] koord; public double[][] vkoord; public double[][] akoord; public double dt=0.01f; public double ax=0.0; //Beschleunigungsvektor in x- und y- Richtung public double ay=0.0; public double C; //Federkonstante public double D; //D\u00e4mpfungskonstante public Random zufall; //public Tonspieler tonspieler; public void setup() { orientation(PORTRAIT); //size(500,500); //im Android-Mode entfernen //Gem\u00e4\u00df den Abmessungen des Bildschrims die ganze Anwendung ausrichten: KUGELBREITE = width/20; if(height<width) KUGELBREITE = height/20; dHALBKUGEL=KUGELBREITE/2; dKUGELBREITE = (double)KUGELBREITE; int maxwert = (int)(width/KUGELBREITE) * (int)(height/KUGELBREITE); KUGELANZAHL = (maxwert*10)/100; zufall = new Random(System.currentTimeMillis()); farbe = new int[KUGELANZAHL][3]; ton = new int[KUGELANZAHL]; kollision = new boolean[KUGELANZAHL]; kollision_alt = new boolean[KUGELANZAHL]; for(int i=0;i<farbe.length;i++) { double alfa = 2.0f*Math.PI*(double)i/(double)(farbe.length-1); double beta = 3.0f*Math.PI*(double)i/(double)(farbe.length-1); double gamma = 5.0f*Math.PI*(double)i/(double)(farbe.length-1); farbe[i][0] = 40+(int)(100.0f*(1.0f+Math.sin(alfa))); farbe[i][1] = 40+(int)(100.0f*(1.0f+Math.cos(alfa))); farbe[i][2] = 40+(int)(100.0f*(1.0f+Math.sin(alfa))); } for(int i=0;i<ton.length;i++) { ton[i] = pentaton[i%pentaton.length]; kollision[i]=false; kollision_alt[i]=false; } koord = new double[KUGELANZAHL][2]; ikoord = new int[KUGELANZAHL][2]; akoord = new double[KUGELANZAHL][2]; vkoord = new double[KUGELANZAHL][2]; //Initialisierungen C=500.0f; D=2.0f; for(int i=0;i<KUGELANZAHL;i++) { int x = 0; int y = 0; boolean schonda=false; do { schonda=false; x = KUGELBREITE/2+KUGELBREITE * zufall.nextInt(width/KUGELBREITE); y = KUGELBREITE/2+KUGELBREITE * zufall.nextInt(height/KUGELBREITE); for(int k=0;k<i;k++) if(x==ikoord[k][0] && y==ikoord[k][1]) schonda=true; } while(schonda==true); ikoord[i][0]=x; ikoord[i][1]=y; koord[i][0]=(double)x; koord[i][1]=(double)y; } //tonspieler = new Tonspieler(); //tonspieler.start(); (new Thread((new Simulation()))).start(); (new KetaiSensor(this)).start(); //erst im Android-Mode aktivieren //frameRate(25); // im Android-Mode entfernen } public void draw() { clear(); background(255); noStroke(); for(int i=0;i<KUGELANZAHL;i++) { fill(farbe[i][0],farbe[i][1],farbe[i][2]); //ellipse(ikoord[i][0]+KUGELBREITE/2,ikoord[i][1]+KUGELBREITE/2,KUGELBREITE,KUGELBREITE); ellipse(ikoord[i][0],ikoord[i][1],KUGELBREITE,KUGELBREITE); } } public void euler() { for(int i=0;i<KUGELANZAHL;i++) { //Erdbescleunigung akoord[i][0] = ax; akoord[i][1] = ay; //linke Wand if(koord[i][0]<dHALBKUGEL) akoord[i][0]+= C*(dHALBKUGEL-koord[i][0]); //rechte Wand if(koord[i][0]>width-dHALBKUGEL) akoord[i][0]+= C*((width-dHALBKUGEL)-koord[i][0]); //obere Wand if(koord[i][1]<dHALBKUGEL) akoord[i][1]+= C*(dHALBKUGEL-koord[i][1]); //untere Wand if(koord[i][1]>height-dHALBKUGEL) akoord[i][1]+= C*((height-dHALBKUGEL)-koord[i][1]); //Kollisionsdetektion kollision[i]=false; for(int k=0;k<KUGELANZAHL;k++) { if(i!=k) { double dx = koord[i][0]-koord[k][0]; double dy = koord[i][1]-koord[k][1]; if(dx*dx+dy*dy<dKUGELBREITE*dKUGELBREITE) { double laenge = Math.sqrt(dx*dx+dy*dy); double kraft = dKUGELBREITE - laenge; if(laenge>0.01f) { akoord[i][0]+=C*(kraft*dx/laenge); akoord[i][1]+=C*(kraft*dy/laenge); kollision[i]=true; } } } } if(kollision[i]==true && kollision_alt[i]==false) { //tonspieler.spieleTon(ton[i]); } kollision_alt[i] = kollision[i]; //allgemeine D\u00e4mpfung akoord[i][0]-=D*vkoord[i][0]; akoord[i][1]-=D*vkoord[i][1]; vkoord[i][0]+=akoord[i][0]*dt; vkoord[i][1]+=akoord[i][1]*dt; koord[i][0]+=vkoord[i][0]*dt; koord[i][1]+=vkoord[i][1]*dt; ikoord[i][0] = (int)(koord[i][0]+0.5f); ikoord[i][1] = (int)(koord[i][1]+0.5f); } } public void onAccelerometerEvent(float x, float y, float z) { ax=-100.0*x; ay=100.0*y; } private class Simulation implements Runnable { public void run() { while(true) { euler(); try { Thread.sleep(10); } catch(Exception e) { } } } } /* public class Tonspieler { private PApplet pap; private Synthesizer synth = null; private MidiChannel[] channels = null; private int kanal_aktuell = 0; private int kanal_max = 9; public void spieleTon(int midihoehe) { channels[kanal_aktuell].allNotesOff(); channels[kanal_aktuell].noteOn(midihoehe, 100); kanal_aktuell++; kanal_aktuell%=kanal_max; } public void spieleTon(int midihoehe, int laute) { channels[kanal_aktuell].allNotesOff(); channels[kanal_aktuell].noteOn(midihoehe, laute); kanal_aktuell++; kanal_aktuell%=kanal_max; } public void spieleTon(int midihoehe, int laute, int kanal) { channels[kanal].allNotesOff(); channels[kanal].noteOn(midihoehe, laute); } public void spieleTon(int midihoehe, int laute, int kanal, int dauer) { spieleTon(midihoehe,laute,kanal); } public void spieleTon(int midihoehe, int laute, int kanal, int dauer, int pitch_cent) { spieleTon(midihoehe,laute,kanal); } public void allesAus() { for(int i=0;i<channels.length;i++) channels[i].allNotesOff(); } public void allesAus(int kanal) { channels[kanal].allNotesOff(); } public void start() { System.out.println("feature2: start synth"); try { synth = MidiSystem.getSynthesizer(); synth.open(); channels = synth.getChannels(); channels[0].programChange(0, 0); channels[1].programChange(0, 2); channels[2].programChange(0, 5); channels[3].programChange(0, 0); channels[4].programChange(0, 2); channels[5].programChange(0, 5); channels[6].programChange(0, 0); channels[7].programChange(0, 2); channels[8].programChange(0, 5); //Pan, also Richtung unterschiedlich setzen: channels[0].controlChange(10, 20);//0..127 channels[1].controlChange(10, 64); channels[2].controlChange(10, 100); channels[3].controlChange(10, 20);//0..127 channels[4].controlChange(10, 64); channels[5].controlChange(10, 100); channels[6].controlChange(10, 20);//0..127 channels[7].controlChange(10, 64); channels[8].controlChange(10, 100); } catch (Exception e) { } allesAus(); } public void stop() { System.out.println("feature2: stop synth"); allesAus(); synth.close(); } } */ }
Code 0-2: Rassel-App im Android-Mode nach dem Importieren nach Eclipse.
Jetzt wieder mit Sound:
package processing.test.kugel2; import processing.core.*; import processing.data.*; import processing.event.*; import processing.opengl.*; import ketai.sensors.*; import java.util.Random; //import javax.sound.midi.MidiSystem; //import javax.sound.midi.Synthesizer; //import javax.sound.midi.MidiChannel; import java.util.HashMap; import java.util.ArrayList; import java.io.File; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; import android.media.SoundPool; import android.media.AudioManager; import android.media.SoundPool.OnLoadCompleteListener; public class kugel2 extends PApplet { public int KUGELBREITE; public int KUGELANZAHL; public double dHALBKUGEL; public double dKUGELBREITE; public int[][] farbe; public int[] ton; public int[] pentaton = {62,64,67,69,71, 74,76,79,81,83,86,88}; public boolean[] kollision; public boolean[] kollision_alt; public int[][] ikoord; public double[][] koord; public double[][] vkoord; public double[][] akoord; public double dt=0.01f; public double ax=0.0; //Beschleunigungsvektor in x- und y- Richtung public double ay=0.0; public double C; //Federkonstante public double D; //D\u00e4mpfungskonstante public Random zufall; //public Tonspieler tonspieler; private SoundPool soundPool; public void setup() { orientation(PORTRAIT); //size(500,500); //im Android-Mode entfernen //Gem\u00e4\u00df den Abmessungen des Bildschrims die ganze Anwendung ausrichten: KUGELBREITE = width/20; if(height<width) KUGELBREITE = height/20; dHALBKUGEL=KUGELBREITE/2; dKUGELBREITE = (double)KUGELBREITE; int maxwert = (int)(width/KUGELBREITE) * (int)(height/KUGELBREITE); KUGELANZAHL = (maxwert*10)/100; zufall = new Random(System.currentTimeMillis()); farbe = new int[KUGELANZAHL][3]; ton = new int[KUGELANZAHL]; kollision = new boolean[KUGELANZAHL]; kollision_alt = new boolean[KUGELANZAHL]; for(int i=0;i<farbe.length;i++) { double alfa = 2.0f*Math.PI*(double)i/(double)(farbe.length-1); double beta = 3.0f*Math.PI*(double)i/(double)(farbe.length-1); double gamma = 5.0f*Math.PI*(double)i/(double)(farbe.length-1); farbe[i][0] = 40+(int)(100.0f*(1.0f+Math.sin(alfa))); farbe[i][1] = 40+(int)(100.0f*(1.0f+Math.cos(alfa))); farbe[i][2] = 40+(int)(100.0f*(1.0f+Math.sin(alfa))); } pentaton[0]=R.raw.ton62; pentaton[1]=R.raw.ton64; pentaton[2]=R.raw.ton67; pentaton[3]=R.raw.ton69; pentaton[4]=R.raw.ton71; pentaton[5]=R.raw.ton74; pentaton[6]=R.raw.ton76; pentaton[7]=R.raw.ton79; pentaton[8]=R.raw.ton81; pentaton[9]=R.raw.ton83; pentaton[10]=R.raw.ton86; pentaton[11]=R.raw.ton88; for(int i=0;i<ton.length;i++) { ton[i] = pentaton[i%pentaton.length]; kollision[i]=false; kollision_alt[i]=false; } koord = new double[KUGELANZAHL][2]; ikoord = new int[KUGELANZAHL][2]; akoord = new double[KUGELANZAHL][2]; vkoord = new double[KUGELANZAHL][2]; //Initialisierungen C=500.0f; D=2.0f; for(int i=0;i<KUGELANZAHL;i++) { int x = 0; int y = 0; boolean schonda=false; do { schonda=false; x = KUGELBREITE/2+KUGELBREITE * zufall.nextInt(width/KUGELBREITE); y = KUGELBREITE/2+KUGELBREITE * zufall.nextInt(height/KUGELBREITE); for(int k=0;k<i;k++) if(x==ikoord[k][0] && y==ikoord[k][1]) schonda=true; } while(schonda==true); ikoord[i][0]=x; ikoord[i][1]=y; koord[i][0]=(double)x; koord[i][1]=(double)y; } //tonspieler = new Tonspieler(); //tonspieler.start(); //maximal KUGELANZAHL Sounds gleichzeitig: // soundPool = new SoundPool(KUGELANZAHL, AudioManager.STREAM_MUSIC, 100); soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0); //IDs laden pentaton[0] = soundPool.load(this, R.raw.ton62, 1); pentaton[1] = soundPool.load(this, R.raw.ton64, 2); pentaton[2] = soundPool.load(this, R.raw.ton67, 3); pentaton[3] = soundPool.load(this, R.raw.ton69, 4); pentaton[4] = soundPool.load(this, R.raw.ton71, 5); pentaton[5] = soundPool.load(this, R.raw.ton74, 6); pentaton[6] = soundPool.load(this, R.raw.ton76, 7); pentaton[7] = soundPool.load(this, R.raw.ton79, 8); pentaton[8] = soundPool.load(this, R.raw.ton81, 9); pentaton[9] = soundPool.load(this, R.raw.ton83, 10); pentaton[10] = soundPool.load(this, R.raw.ton86, 11); pentaton[11] = soundPool.load(this, R.raw.ton88, 12); (new Thread((new Simulation()))).start(); (new KetaiSensor(this)).start(); //erst im Android-Mode aktivieren //frameRate(25); // im Android-Mode entfernen } public void draw() { clear(); background(255); noStroke(); for(int i=0;i<KUGELANZAHL;i++) { fill(farbe[i][0],farbe[i][1],farbe[i][2]); //ellipse(ikoord[i][0]+KUGELBREITE/2,ikoord[i][1]+KUGELBREITE/2,KUGELBREITE,KUGELBREITE); ellipse(ikoord[i][0],ikoord[i][1],KUGELBREITE,KUGELBREITE); } } public void euler() { for(int i=0;i<KUGELANZAHL;i++) { //Erdbescleunigung akoord[i][0] = ax; akoord[i][1] = ay; //linke Wand if(koord[i][0]<dHALBKUGEL) akoord[i][0]+= C*(dHALBKUGEL-koord[i][0]); //rechte Wand if(koord[i][0]>width-dHALBKUGEL) akoord[i][0]+= C*((width-dHALBKUGEL)-koord[i][0]); //obere Wand if(koord[i][1]<dHALBKUGEL) akoord[i][1]+= C*(dHALBKUGEL-koord[i][1]); //untere Wand if(koord[i][1]>height-dHALBKUGEL) akoord[i][1]+= C*((height-dHALBKUGEL)-koord[i][1]); //Kollisionsdetektion kollision[i]=false; for(int k=0;k<KUGELANZAHL;k++) { if(i!=k) { double dx = koord[i][0]-koord[k][0]; double dy = koord[i][1]-koord[k][1]; if(dx*dx+dy*dy<dKUGELBREITE*dKUGELBREITE) { double laenge = Math.sqrt(dx*dx+dy*dy); double kraft = dKUGELBREITE - laenge; if(laenge>0.01f) { akoord[i][0]+=C*(kraft*dx/laenge); akoord[i][1]+=C*(kraft*dy/laenge); akoord[i][0]-=D*(vkoord[i][0]*dx/laenge); akoord[i][1]-=D*(vkoord[i][0]*dy/laenge); kollision[i]=true; } } } } if(kollision[i]==true && kollision_alt[i]==false) { //tonspieler.spieleTon(ton[i]); float volume = 1.0f;// whatever in the range = 0.0 to 1.0 // play sound with same right and left volume, with a priority of 1, // zero repeats (i.e play once), and a playback rate of 1f // soundPool.play(pentaton[i], volume, volume, 1, 0, 1f); try { soundPool.play(pentaton[i], 0.5f, 0.5f, 0, 0, 1f); } catch(Exception eee) { System.out.println("FEHLER:"+eee); } } kollision_alt[i] = kollision[i]; //allgemeine D\u00e4mpfung akoord[i][0]-=D*vkoord[i][0]; akoord[i][1]-=D*vkoord[i][1]; vkoord[i][0]+=akoord[i][0]*dt; vkoord[i][1]+=akoord[i][1]*dt; koord[i][0]+=vkoord[i][0]*dt; koord[i][1]+=vkoord[i][1]*dt; ikoord[i][0] = (int)(koord[i][0]+0.5f); ikoord[i][1] = (int)(koord[i][1]+0.5f); } } public void onAccelerometerEvent(float x, float y, float z) { ax=-100.0*x; ay=100.0*y; } private class Simulation implements Runnable { public void run() { while(true) { euler(); try { Thread.sleep(10); } catch(Exception e) { } } } } }
Code 0-3: Rassel-App als Eclipse-Projekt unter Verwendung von Android-Sound-Klassen.