Java Schwierigkeiten Sound in mein Pong Spiel ein zu bauen

Zespire

Lieutenant
Dabei seit
März 2007
Beiträge
701
Grüße mal wieder muss Ich euch belästigen, dieses mal geht es um den Sound.

Ich habe in mein Pong Spiel jetzt einen einfachen Audio Clip eingebaut, der mir einiges an Ärger macht.

Hier eine Liste der Probleme:

1.Die Audiodatei muss jedes mal neu geladen werden.

2.Wen ich den Clip nicht stoppe sinken meine FPS um 15.

3.Die einzige Möglichkeit die ich gefunden habe den Clip zu stoppen pausiert das ganze Spiel weil while.

Code:
	while(clip.getMicrosecondLength() != clip.getMicrosecondPosition()){}
        	clip.stop();
Hier meine GameLoop die relevanten Zeilen sind 3, 22, und 70.

Code:
public class GameLoop {
	
	private Sound    sound    = new Sound();
	
	private Window   window   = new Window();
	private Menu	 menu	  = new Menu();
	private Ball 	 ball     = new Ball();
	private Block [] block    = new Block[2];
	private byte     fps	  = 0;
	
	public void mainLoop(){
		
		final byte TARGET_FPS 	= 60;
		final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
		
		float      delta		= 0;		
		long 	   lastLoopTime = System.nanoTime();		
		long       lastFPStime 	= 0;
		byte 	   fps 			= 0;
		
		window.loadFiles();
		sound.loadFiles();
		
		menu.init(window.menu,window.screenBounds);
		block[0] =new Block();
		block[1] =new Block();
		ball.init(window.screenBounds, window.background, window.ball);
		block[0].init(false, window.screenBounds, window.block, window.background);
		block[1].init(true, window.screenBounds, window.block, window.background);
		
		while(true){
			
			long now = System.nanoTime();
			long updateLength = now - lastLoopTime;
			lastLoopTime = now;
			
			//---------FPS COUNTER-------
			lastFPStime += updateLength;
			fps++;
			if(lastFPStime >= 1000000000){
				System.out.println(fps);
				this.fps = fps;
				fps = 0;
				lastFPStime = 0;
			}
			
			//------Update the Game------
			delta = (float) (updateLength / ((double)OPTIMAL_TIME));
			gameMath(delta);
			render();		
			
			//-----------Sleep-----------
			if((lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000 >= 1){
				try {
						Thread.sleep((lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000);
					} catch (InterruptedException e){e.printStackTrace();}	
			}
		}

	}
	
	
	
	private void gameMath(float delta){
		
		ball.move(delta,block);
		if(ball.colide){
			
		//	sound.loadFiles();
			sound.playSound();
			block[0].getComputerTarget(delta, ball.size, ball.angle, ball.position);
			ball.colide = false;
		}
		block[0].moveComputer(delta,ball.position);
		block[1].movePlayer(delta, window.WSAD);

	}
	
	
	
	private void render(){
		
		window.draw(ball, block, fps, menu);
		
	}	
	
	
	
}


Und hier meine Sound Klasse.
Code:
public class Sound {
	
	private AudioInputStream testsound ;
	
	public void loadFiles(){
		try {
			
			testsound =  AudioSystem.getAudioInputStream(getClass().getClassLoader().getResourceAsStream("SOUND/altemark__pong.wav"));
		
		} catch (UnsupportedAudioFileException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
	}
	
	public void playSound(){
		try {
			
			Clip clip = AudioSystem.getClip();
			clip.open(testsound);
			clip.start();
		//	while(clip.getMicrosecondLength() != clip.getMicrosecondPosition()){}
		//	clip.stop();
			
		} catch (LineUnavailableException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
	}
	
	
	
}
 

blackst0rm

Ensign
Dabei seit
März 2012
Beiträge
182
1.
Warum erzeugst du das Clip Objekt nicht bei der Konstruktion des Sound-Objekts?
Währe im übrigen schöner die Ressourcen in einem Konstruktor zu laden als in einer Klassenmethode dafür is der da.

Ich kenne mich mit dem AudioSystem in Java nicht aus aber ich vermute das der Clip widerverwendet werden kann und nicht immer wieder erzeugt werden muss.

2.
Nur ein Vermutung.
Ich denke das Problem könnte man adressieren in dem der Clip in einem eigenen Thread abgespielt wird. Da der momentan in im Hauptthread läuft in dem auch die Grafik erzeugt wird zieht der natürlich an der Leistung. (Grundsätzlich so Sachen wie Audio Playback oder blockierenden Netzwerkverkehr NIEMALS! im UI-Thread machen.)

3.
Audio im eigenen Thread adressiert das Problem gleich mit :)
da der Thread unabhängig vom ui thread ist (und dank Multicore-Prozessoren sogar echt parallell sein sollte) kannst da drinne warten wie du lust hast.

Ich hoffe das hilft dir

Edit: Fehler behoben...
 

Zespire

Lieutenant
Ersteller dieses Themas
Dabei seit
März 2007
Beiträge
701
Schon einmal danke für deine Antwort das mit den Konstruktoren werde mich zu Herzen nehmen.

Zu erstens der Clip lässt wieder verwenden muss ihn nur zurück auf 0 spulen :D

Code:
public class Sound {
	Clip clip = null;
	
	public void loadFiles(){
		try {
			
			clip = AudioSystem.getClip();
			clip.open(AudioSystem.getAudioInputStream(getClass().getClassLoader().getResourceAsStream("SOUND/altemark__pong.wav")));
			
		} catch (UnsupportedAudioFileException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (LineUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void playSound(){
		
		clip.setFramePosition(0);
		clip.start();
		
	//while(clip.getMicrosecondLength() != clip.getMicrosecondPosition()){}
	//clip.stop();
	
	}
	
}

Zweitens und Drittens werde ich Morgen angehen, hatte mir schon Gedanken zu Multithreading gemacht aber mich noch nicht damit auseinander gesetzt.
 

e-Laurin

Fleet Admiral
Dabei seit
Juni 2005
Beiträge
10.497
Zu 2.:
Wenn du das in einen extra Thread packst, dann überlege dir, ob du einen oder mehrere Threads haben willst.

Ein Thread würde bedeuten, dass du einen bestimmten Sound nur einmal gleichzeitig abspielen kannst. Die Clip.start()-Methode selber würde gar nichts machen, wenn der Clip bereits läuft. Ein "Zurückspulen" eines bereits abspielenden Sounds hört sich auch nicht besonders an.

Mehrere Threads erlauben dir die gleichen Sounds gleichzeitig und unabhängig voneinander abzuspielen. Allerdings benötigt das Ressourcen- und Threadmanagement. Du musst dafür sorgen, dass jeder Thread sein eigenes Clip-Objekt hat. Das ist aber leicht lösbar, nur etwas umfangreicher. Dieser Ansatz ist flexibler, aber kommt in die Gegend, wo man mit Kanonen auf Spatzen schießt. ^^
Du bräuchtest hier eine Klasse für den Thread (es reicht dafür, das Interface Runnable zu implementieren), eine Klasse, die die Threads steuert (oder einen ThreadPoolExecutor verwenden) und eine Klasse, die für jeden Thread die benötigten Ressourcen (Clips) zur Verfügung stellt (static synchronized-Methoden reichen hier aus).
 

Zespire

Lieutenant
Ersteller dieses Themas
Dabei seit
März 2007
Beiträge
701
Also ich habe noch einmal etwas rum probiert ohne extra Threads und dabei etwas Seltsames festgestellt.

Also folgendes passiert wen ich die TARGET_FPS in Zeile 13 Verstelle.



TARGET_FPS 60 und Sound an das Spiel läuft mit 45 FPS
TARGET_FPS 60 und Sound aus das Spiel läuft mit 60 FPS

TARGET_FPS 75 und Sound an das Spiel läuft mit 100 FPS
TARGET_FPS 75 und Sound aus das Spiel läuft mit 65 FPS

TARGET_FPS 90 und Sound an das Spiel läuft mit 90 FPS
TARGET_FPS 90 und Sound aus das Spiel läuft mit 60 FPS
 

e-Laurin

Fleet Admiral
Dabei seit
Juni 2005
Beiträge
10.497
Nanosekunden = 10^-9 Sekunden
Genauigkeit eines Floats = ca. 6 Stellen hinterm Komma

Und du castest bei der Zeitberechnung fleißig zwischen Long, Float und Double hin und her. Dir geht da massiv viel Genauigkeit verloren. Mach diese ganze Rechnung nur in Longs. Es gibt keinen Grund, an dieser Stelle Gleitkommazahlen zu verwenden.

Ansonsten gilt noch
Zitat von http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#nanoTime():
This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of currentTimeMillis().
Btw, currentTimeMillis() läuft unter Windows, glaub ich, in einer 14,2 ms Auflösung (interner Takt der Prozessteuerung). Im worst case könntest du die Ziel-FPS nur auf ~70 fps stellen. Darüber bringt nichts, da er nicht schnell genug die Zeit aktualisiert.

Edit:
Korrektur: Das schwankt zwischen 10 und 16 ms. (Quelle)
Du musst dir einen anderen Zeitgeber suchen. Beide Funktionen sind nicht zuverlässig genug für hohe FPS.
 
Zuletzt bearbeitet:
Top