Physik Simulation

Jaiel

Jaiel

Dauergast
235
Hey hat jemand Lust mir zu helfen bei einem kleinen Problem?

Ich hab in den letzten 2 Tagen eine kleine Simulation zusammengebastelt und hab einen Bug den ich mir nciht vollständig erklären kann. Der Code liegt zur Zeit nur als eine Java swing applikation vor.

Ihr könnt ja wenn ihr lust habt schnell mal in Eclipse ein neues Projekt anlegen und den Code rein kopieren(Package Namen ändern und die jew. Klassen anlegen)

Es sind nur 2 Klassen eine für die Darstellung in einem JFrame und eine ObjektKlasse:

JFrame Klasse mit Main():
Code:
package ParticleSystem;

import java.awt.Color;
//import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Font;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;



/**
 * 
 * @author Jai3l
 *	
 *
 *	Diese Applikation lässt euch bei einem Mausklick Partikel auf den Bildschirm spawnen mit einer
 *	Gravitationskraft die auf diese wirkt im Mittelpunkt
 *	durch ein klicken und ziehen mit der Maus kann man diese Punkt bewegen und die Gravitation verlagern.
 *	
 *	Die Werte in der Particle Klasse habe ich durch feinjustierung bis es mir gefiel herausbekommen
 *	Das hat auch die meißte Zeit in Anspruch genommen.	
 *	Das Problem ist: der Impuls der Teilchen nimmt mit der Zeit ab!!!
 *
 */
public class ParticleWindow extends JFrame {

	//JFrame erstellen und mit dem Jpanel füllen
	public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                ParticleWindow pw = new ParticleWindow();
                DrawPanel dp=new DrawPanel();
                dp.setBackground(Color.black);
                dp.setIgnoreRepaint(true);
                pw.add(dp);
                //Mein Screen auflösung ist 1280 X 960 müsst ihr ändern auf eurem Screen
                //und zwar an mehreren stellen im code
                pw.setSize(1280,860);
                pw.setTitle("ParticleSystem");
                pw.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                pw.setVisible(true);
                
            }
        });
    }
}

[COLOR="SeaGreen"]//JPanel[/COLOR]
class DrawPanel extends JPanel {

	public int particlesArraySize=100000,mouseX,mouseY,mouseX1,mouseY1;[COLOR="SeaGreen"]//objektanzahl und mouses[/COLOR]
	Particles[] particle=new Particles[particlesArraySize];[COLOR="SeaGreen"]//die Objekte[/COLOR]
	Timer timer;[COLOR="SeaGreen"]//timer[/COLOR]
	long delTime=0,oldTime=0,timeToRender,FPS,fps;[COLOR="SeaGreen"]//zeit und fps variable[/COLOR]
	double fps2;[COLOR="SeaGreen"]//fpsvariable double weil /2[/COLOR]
                          [COLOR="SeaGreen"]//Wenn der Timer feuert wird das hier ausgeführt[/COLOR]
	ActionListener action =new ActionListener()
	{

		public void actionPerformed(ActionEvent arg0) {
			
			[COLOR="SeaGreen"]//deltaTime berechnen[/COLOR]
			if(oldTime!=0)
				delTime=System.currentTimeMillis()-oldTime;
			
			[COLOR="SeaGreen"]//deltime hinzuaddieren[/COLOR]
			oldTime=System.currentTimeMillis();
			timeToRender+=delTime;
			
			[COLOR="SeaGreen"]//wenn mehr als 20 ms vorbei sind wird es time to render;)[/COLOR]
			if(timeToRender>=20d)
			{
				for (int i=0;i<particlesArraySize;i++)
				{
					[COLOR="SeaGreen"]//berechne neue Position aber vorsicht nicht bevor initialisiert wurde!!![/COLOR]
					if(particle[i]!=null)
						
						particle[i].render(2*timeToRender);
				}[COLOR="SeaGreen"]
				//wieder nullen[/COLOR]
				timeToRender=0;
			}[COLOR="SeaGreen"]
			//JPanel neu zeichnen[/COLOR]
			repaint();
			
		}
		
	};
	public DrawPanel()
	{[COLOR="SeaGreen"]
		//timer initialisiern und starten (alle 5 ms um sicher zu sein)[/COLOR]
		timer=new Timer(5,action);
		timer.start();
		[COLOR="SeaGreen"]
		//particles initialisieren(ausgelegt auf Screen density von 1280*960[/COLOR]
		for(int i=0;i<particlesArraySize;i++)
			particle[i]=new Particles(640d,480d);
		[COLOR="SeaGreen"]
		//auf die Mitte festlegen(bei density 1280*960 des screens[/COLOR]
		mouseX=640;
		mouseY=480;
			[COLOR="SeaGreen"]
		//auf Mausklick reagieren:die Particles neu spawnen[/COLOR]
		addMouseListener(new MouseAdapter(){
	    	public void mouseClicked(MouseEvent evt)
	    	{[COLOR="SeaGreen"]
	    		//anklickpunkt speichern[/COLOR]
	    		mouseX1=evt.getX();
	    		mouseY1=evt.getY();
	    		[COLOR="SeaGreen"]
	    		//falls particles aktiv sind deaktivieren und neu spawnen[/COLOR]
	    		for(int i=0;i<particlesArraySize;i++)
	    		{
	    			particle[i].respawn();
	    			particle[i].posX=mouseX1+(Math.random()*1000d-500d);
	    			particle[i].posY=mouseY1+(Math.random()*1000d-500d);
	    			particle[i].active=true;
	    		}
	    	}
		});
		[COLOR="SeaGreen"]
		//auf Mausziehen reagieren: Gravitationspunkt über den Bildschirm ziehen[/COLOR]
		addMouseMotionListener(new MouseAdapter(){
	    	public void mouseDragged(MouseEvent evt)
	    	{
	    		mouseX=evt.getX();
	    		mouseY=evt.getY();
	    		for(int i=0;i<particlesArraySize;i++)
	    		{
	    			particle[i].gX=mouseX;
	    			particle[i].gY=mouseY;
	    		}
	    	}
		});
		
	}
	
    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;
        [COLOR="SeaGreen"]
        //Particles bekommen eine schöne blaue Farbe[/COLOR]
        g2d.setColor(Color.blue);
        for(int i=0;i<particlesArraySize;i++)
        {[COLOR="SeaGreen"]
        	//hier kann man zwischen punkten und Kugeln wählen[/COLOR]
        	g2d.drawLine((int)particle[i].posX, (int)particle[i].posY,(int)particle[i].posX, (int)particle[i].posY);[COLOR="SeaGreen"]
        	//g2d.fillOval((int)particle[i].posX-15, (int)particle[i].posY-15,30, 30);[/COLOR]
        }
        [COLOR="SeaGreen"]
        //Gravitationspunkt malen[/COLOR]
        g2d.setColor(Color.black);
        g2d.fillOval(mouseX-20,mouseY-20,41,41);
        
        g2d.setColor(Color.white);
        [COLOR="SeaGreen"]
        //FPS oben anzeigen[/COLOR]
		g2d.setFont(new Font("hi", Font.PLAIN, 12));
		g2d.drawString("FPS : "+((int)fps2), 480, 20);
[COLOR="SeaGreen"]
		//die FPS berechnen nach 2 sekunden[/COLOR]
		fps++;
		FPS+=delTime;
		if(FPS>=2000)
		{
			fps2=fps/2;
			fps=0;
			FPS=0;
		}
    }

   [COLOR="SeaGreen"] //wird beim repaint() aufgerufen und ruft ihrerseits die obere funktion auf
   [/COLOR] @Override
    public void paintComponent(Graphics g) {
        
        super.paintComponent(g);
        doDrawing(g);
    }
}

Und hier die Objekt Klasse:
Code:
package ParticleSystem;

import java.awt.Color;

public class Particles {
	
	double posX;[COLOR="SeaGreen"]//position in x[/COLOR]
	double posY;[COLOR="SeaGreen"]//position in y[/COLOR]
	double gX=0;[COLOR="SeaGreen"]//position der graviatation in x[/COLOR]
	double gY=0;[COLOR="SeaGreen"]//position der gravitation in y[/COLOR]
	double a=1d/3000d;[COLOR="SeaGreen"]//beschleunigung(immer gleich, ja ich weiß newton und so)[/COLOR]
	double velX=(Math.random()/10000d-1d/20000d);[COLOR="SeaGreen"]//zufällige x geschwindigkeit[/COLOR]
	double velY=(Math.random()/10000d-1d/20000d);[COLOR="SeaGreen"]//zufällige y geschwindigkeit[/COLOR]
	double dVX;[COLOR="SeaGreen"]//delta geschwindigkeit in x[/COLOR]
	double dVY;[COLOR="SeaGreen"]//delta geschwindigkeit in y[/COLOR]
	double delPos;[COLOR="SeaGreen"]//abstand vom gravitations punkt[/COLOR]
	boolean active=false;[COLOR="SeaGreen"]//ist es aktiv?[/COLOR]
	
	public Particles(double gX,double gY)
	{
		this.posX=0;
		this.posY=0;
		this.gX=gX;
		this.gY=gY;
	}
	
	public void respawn()
	{
		velX=(Math.random()/10000d-1d/20000d);
		velY=(Math.random()/10000d-1d/20000d);
		dVX=0;
		dVY=0;
		delPos=0;
	}
	
	[COLOR="SeaGreen"]
	//die Methode mit der ich die Position des jew. Partikels berechne[/COLOR] 
	public void render(double delTime)
	{
		if(active==true)
		{
			[COLOR="SeaGreen"]//abstand von der Gravitation berechnen und beschriebt die hypotenuse[/COLOR]
			delPos=Math.sqrt((gX-posX)*(gX-posX)+(gY-posY)*(gY-posY));
			
			[COLOR="SeaGreen"]//dies hat keinen logischen Hintergrund sondern ich wollte einfach verhindern das delTime in den 
			//folgenden Fomreln doppelt gerechnet wird
			[/COLOR]delPos/=delTime;
			
			[COLOR="SeaGreen"]//falls der wert zu klein wird neu justieren um keine unendlichen beschleunigungen zu erhalten
			//je größer der Wert desto näher ist es irgendwie an der realität dran[/COLOR]
			if(delPos<5d)
				delPos=5d;
			
			[COLOR="SeaGreen"]//um wieviel hat sich die Geschwindigkeit während dem letzten abruf durch die beschleunigung
			//geändert(delTime wurde ja schon mit einberechnet in delPos)
			//dVX/dVY ist die geschwindigkeitsänderung in x bzw. y Richtung
			//Formel: dVX=a*t*cos alpha also dVX=a*delTime*cos alpha
			//cos alpha=ankathete/hypotenuse also (gX-posX)/delPos
			//dasselbe mit dVY nur nimmt man dort den sin alpha
			[/COLOR]dVX=a*(gX-posX)/delPos;
			dVY=a*(gY-posY)/delPos;
			
			[COLOR="SeaGreen"]//die geschwindigkeitsänderungen wird in die derzeitige geschwindigkeit mit einberechnet[/COLOR]
			velX+=dVX;
			velY+=dVY;
			[COLOR="SeaGreen"]
			//und jetzt wird geupdatet
			//Formel(bei konstanter Beschleunigung
			//s=s0 + delta v/2*t + v*t (delta v/2*t weil a=v/t!!!)
			[/COLOR]this.posX=this.posX+(dVX/2+velX)*(delTime);
			this.posY=this.posY+(dVY/2+velY)*(delTime);
		}
	}
}



Nun das Problem ist dass die Partikel ihren Impuls nach der Zeit verlieren und ich kann mir das kaum erklären: Kann es am resolutionwert des Doubles liegen? der hat ja auch leider seine Grenzen nach der 15. Stelle nach dem Komma!!!
 
Zuletzt bearbeitet:
Falls es jemand wissen möchte ich hab es gelöst: meine Formel hatte leider ein flüchtigkeitsfehler und bei physikalischen systemen ist es immer besonders wichtig dass die positionsupdates, also die positionsiterationen möglichst groß sind ich berechne jetzt die position pro 0.01 ms

Dadurch kann ich dann keine 200.000 partikel mehr rendern weil das wiederum zu viel rechenzeit kostet und mein PC an seine Grenzen kommt und die delta zeit sich dann hochhauschaukelt und deswegen müssen wieder mehr berechet schritte ebrechnet werden das wiederums chaukelt sich dann auch hoch womit bei jedem rendern eine immer höhere berechnugnszeit reinkommt....also ihr versteht schon es schaukelt sich einfach hoch und das programm bleibt stecken :D

Naja aber für 2000 Partikel reicht es allemal

Ich arbeite zur zeit noch an einer Lösung um es wieder für 100.000 partikel hinzukriegen aber das wären zu viele Details dachte das das jemand heir interressieren würde ;)

Ciao
 

Ähnliche Themen

GvGHunter
Antworten
4
Aufrufe
791
GvGHunter
GvGHunter
B
  • Beluvin
Antworten
1
Aufrufe
649
Jaiel
Jaiel
N
Antworten
3
Aufrufe
1.044
N2k1
N2k1
Zurück
Oben Unten