speicherschonendere lösung?

darkeye2

Bekanntes Mitglied
Hallo,

kaum hat sich ein problem gelöst, schon hab ich bei einem anderen programm ein neus Problem^^

Also ich habe ein feld, dass sehr viele punkte beinhalten soll (anzahl variiert zwischen 100 und 10000)
nun kann jeder punkt eigentlich 4 zustände haben:
-unset
-unset_selected
-set_blue
-set_red
jeder dieser zustände wird durch ein bild repräsentiert, welches an der entsprechenden stelle des feldes sein soll.

Mein Problem: momentan stelle ich jeden punkt durch ein JLabel da, solnge ein punkt unset ist hat er einen mouselistener und änderst sein icon, wenn man mit der maus drüber geht und wenn man ihn anklickt wird das label gelöscht und ein neues wird erstellt, dass dann ein set repräsentiert (hat je nach farbe eine anderes icon).
Bei 1000 JLabel ist es schon etwas viel und sehr speicherfressend, deswegen will ich das jetzt anders machen, hat jemand eine Idee, wie man das am besten macht?

MfG
darkeye
 
Du kannst das ganze z.b. auf ein BufferedImage malen und dann dieses BufferedImage anzeigen. Dann wird aber natürlich etwas schwieriger mit den MouseListenern, du müsstest dann die Koordinaten der Maus auf dein Feld umrechnen.
 
hmm, wie kann ich ein bufferedImage mit transparenten hintergrund erzeugen? (weil auf meienen jpanel ist bereits ein feld aufgezeichnet, dass man trotzdem noch sehen soll)
.... Ja, das mit dem MousListener kann ich mir bei einem BufferedImage net ganz vorstellen, immer wenn die maus bewegt wird müsste man ja neu berechnen, ob überhaupt ein punkt selectiert ist, wenn ja müsste man diese stelle neu zeichnen und wenn gedrückt wird schon wieder das ganze bild neu zeichnen?
 
hmm, wie kann ich ein bufferedImage mit transparenten hintergrund erzeugen?
Da bin ich überfragt. Evtl. kannst du den Frame dazu bringen sich auf deinem BufferedImage zu zeichnen, dadurch würde das bufferedimage durchsichtig erscheinen.
Aber vllt. hat jemand anderes ja noch ne bessere idee 🙂
 
Nimm eine JComponent. Der übergibst du ein Modell das deine Punkte repräsentiert. Die JComponent zeichnet den Zustand dieses Modells in paintComponent. Um den Mouse Listener musst du dich tatsächlich selbst kümmern denn 10000 Labels sind einfach 'too much'
 
Hm. Natürlich sollte man Modell und View trennen, aber in diesem Fall könnte man (eventuell! Je nachdem, was damit am ENde erreicht werden soll!) so weit gehen, das BufferedImage als "Modell" anzusehen. Wie man einen int[1024*768] effizient auf den Bildschirm bringen soll, ohne ihn in ein BufferedImage einzuwickeln, wüßte ich jetzt nicht.

Insgesamt klingt das nach sowas (nur schnell hingeschrieben)
Java:
class ThePanel extends JPanel implements MouseListener
{
    private BufferedImage image = ....

    public ThePanel()
    {
        setOpaque(false);
    }
    public void paintComponent(Graphics g)
    {
        g.drawImage(image,0,0,this); // Bild im Hintergrund
        super.paintComponent(g); // Buttons usw. im Vordergrund...
    }
    public void mouseClicked(MouseEvent e)
    {
        int rgb = image.getRGB(e.getX(), e.getY());
        int newRGB = ...
        image.setRGB(e.getX(), e.getY(), newRGB);
    }
}
 
grundsätzlich würd ich schon JLabel verwenden wenn das klappt.
ich würde jedoch die das feld als int oder short array halten.

beim zeichnen ein JLabel instanzieren.
ersten wert setzen, zeichnen, zweiten wert setzen, zeichnen .....
Eine JTable macht es auch so, die ganzen Felder der Tabelle sind in Wirklichkeit immer das gleiche das wiederverwendet wird...
 
Schon mal danke für die Ideen,werde mir da mal gedanken machen, um nochmal genau zu verdeutlichen, worum es geht, hier eine etwas ausführlicherere erklärung:
Es geht um ein spielfeld, das ganze spielfeld besteht aus einen hintergrund (kästchen, die größe wird vom aktuellen zoomfactor bestimmt)
Und eben an jedem eckpunkt ein der kästchen soll ein punkt sizten, diese hab ich bereits als bilder vorbereitet. Zu Anfang sind alle punkte weiß also wird wird das bilde white_small.png an jeden eckpunkt gesetzt, bei dürbergehen mit der Maus über einen weißen punkt soll er einen gelben rahmen riegen (also an stelle von white_small.png soll jetzt selected_small.png an der entsprechenden stelle sein) wird nun draufgeklickt soll der punkt durch die farbe des spielers ersetzt werden. also entweder red_small.png oder blue_small.png diese bilder sollen nicht mehr auf klicks oder mouseOver reagieren.

MfG
darkeye
 
Ich mache wohl irgendwas falsch, bei mir ist der speicherverbrauch kein bisschen gesunken, obwohl ich jetzt ein bild zeichne ...
code:
Java:
	OpperatingField(int s){
		size = s+1;
		field = new boolean[size][size];
		redField = new boolean[size][size];
		blueField = new boolean[size][size];
		setImage(size);
		preparePoints(radius);
		spielFeld = new OpticalPane();
		spielFeld.setBounds(0, 0, gridsize*size+size+10, gridsize*size+size+10);
		this.setPreferredSize(new Dimension(gridsize*size+size+10, gridsize*size+size+10));
		this.setLayout(null);
		this.setVisible(true);
		this.add(spielFeld);
	}
	
	
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.drawImage(background, 0, 0, this);
    }
	
    
    private  void setImage(int size){
    	int iw = gridsize*size;
    	background = new BufferedImage(iw, iw, BufferedImage.TYPE_INT_RGB);
    	gi = background.getGraphics();
    	gi.setColor(Color.white);
    	gi.fillRect(0, 0, iw, iw);
    	gi.setColor(Color.black);
    	for(int i =0; i<size; i++){
    		gi.drawLine(gridsize*i, 0, gridsize*i, iw);
    		//gi.drawLine(gridsize*i+1, 0, gridsize*i+1, iw);
    		gi.drawLine(0, gridsize*i, iw, gridsize*i);
    		//gi.drawLine(0, gridsize*i+1, iw, gridsize*i+1);
    	}
    	
    }

//methode für die punkte:
    private class OpticalPane extends JPanel implements MouseListener{
		private static final long serialVersionUID = -2108627378088929277L;
		private BufferedImage game;
		
		OpticalPane(){
			this.setOpaque(false);
			this.setPreferredSize(new Dimension(gridsize*size+size+10, gridsize*size+size+10));
			createGame();
			repaint();
			
		}
		
	    public void paintComponent(Graphics g)
	    {
	        super.paintComponent(g);
	        g.drawImage(game, 0, 0, this);
	    }
	    
	    private void createGame(){
	    	game = new BufferedImage(gridsize*size, gridsize*size, BufferedImage.TYPE_INT_ARGB_PRE);
	    	Graphics g1 = game.getGraphics();
			for(int i =1; i< size; i++){
				for(int n =1; n< size; n++){
					if(!field[i][n]){
						g1.drawImage(white, (i*gridsize)-radius, (n*gridsize)-radius, this);
					}else if(redField[i][n]){
						g1.drawImage(red, (i*gridsize)-radius, (n*gridsize)-radius, this);
					}else if (blueField[i][n]){
						g1.drawImage(blue, (i*gridsize)-radius, (n*gridsize)-radius, this);
					}
				}
			}
	    	
	    	
	    }
 
keiner ne Idee, woran das liegen kann? wie werden denn bei anderen auch anspuchsvollereren Spielen die Karten dargestellt? es kann doch net sein, dass ich schon bei so nen kleinen Spielfeld 150MB RAM verbrauche.
 
Java:
        field = new boolean[size][size];
        redField = new boolean[size][size];
        blueField = new boolean[size][size];
Spannend wäre jetzt, die du das machen würdest, wenn du 16.7 Millionen Farben darstellen wolltest... ... ... ...

Wie auch immer: Wie "misst du denn den Speicherverbrauch?
 
wegen farben:
da in dem fall nur 2 spieler auf einmal spielen können ist das ne recht angenehme art zu unterscheiden, welche punkte wem gehören ... , würde ich die farbe frei wählbar machen würde ich intern trotzdem redField und blueField verwenden und nach außen hin einfach per JColorChooser die farbe wählen lassen, ist aber hier net der fall.

Speicherverbrauch:
im taskmanager steht da, wie viel ram das prog grad verbraucht ... und je nach feldgröße ändert sich dieser, wobei ich mit jlabels eigentlich einen genau so großen verbrauch hatte ... . Es ist mir ein rätsel, was ich so falsch mache, es gibt in java programmiert Spiele, die riesige welten darstellen, in dennen sich was bewegt und wird weniger ram verbraucht als bei mir beim anzeigen des spielfeldes, das eigentlich gar net so groß ist (zumindest das was ich getestet habe) .

Hoffe es kann jemand helfen.
 
Was der Taskmanager anzeigt ist nur SEHR bedingt (d.h. "nicht" 😉 ) aussagekräftig. Er zeigt in erster Näherung an, wie viel Speicher der VM zugesichert ist. Wie viel tatsächlich (und wofür) belegt ist, erkennt man am ehesten mit einem Profiler. Im Zweifelsfall mit der "VisualVM", die beim JDK dabei ist.
 
ok, danke für den tipp, also die grundsätzlich dinge sind soweit in Ordnugn, ich kann ein Spielfeld erzeugen und ich kann punkte setzen, das Problem, was ich jetzt hab der erste gesetzte punkt ist net da^^ er wird zwar als gesetzt markiert, aber ist trotzdem weiß (und nicht wie es beim test sein sollte blau.

der code für den Mauslistener und die zugehörigen methoden, die für das Problem relefant sein könnten:
Java:
 private class OpticalPane extends JPanel implements MouseListener{
		private static final long serialVersionUID = -2108627378088929277L;
		private BufferedImage game;
		
		OpticalPane(){
			this.setOpaque(false);
			this.setPreferredSize(new Dimension(gridsize*size+size+10, gridsize*size+size+10));
			this.addMouseListener(this);
			createGame();
			repaint();
			
		}
		
	    public void paintComponent(Graphics g)
	    {
	        super.paintComponent(g);
	        g.drawImage(game, 0, 0, this);
	    }
	    
	    private void setPoint(Point p, int mode){
	    	Graphics gF = spielFeld.getGraphics();
	    	if(mode == 1)
	    		gF.drawImage(selected, (p.x*gridsize)-radius, (p.y*gridsize)-radius, this);
	    	if(mode == 2)
		    	gF.drawImage(white, (p.x*gridsize)-radius, (p.y*gridsize)-radius, this);
	    	if(mode == 3){
		    	gF.drawImage(getColor(), (p.x*gridsize)-radius, (p.y*gridsize)-radius, this);
		    	field[p.x][p.y] = true;
		    	
	    	}
	    	
	    }
	    
	    private Image getColor(){
	    	if(yourColor == 1){
	    		return blue;
	    	}else{
	    		return red;
	    	}
	    }
	    
	    private void createGame(){
	    	game = new BufferedImage(gridsize*size, gridsize*size, BufferedImage.TYPE_INT_ARGB_PRE);
	    	Graphics g1 = game.getGraphics();
			for(int i =1; i< size; i++){
				for(int n =1; n< size; n++){
					if(!field[i][n]){
						g1.drawImage(white, (i*gridsize)-radius, (n*gridsize)-radius, this);
					}else if(redField[i][n]){
						g1.drawImage(red, (i*gridsize)-radius, (n*gridsize)-radius, this);
					}else if (blueField[i][n]){
						g1.drawImage(blue, (i*gridsize)-radius, (n*gridsize)-radius, this);
					}
				}
			}
	    	
	    	
	    }
	   

		public void mouseClicked(MouseEvent arg0) {
			
		}

		public void mouseEntered(MouseEvent arg0) {
			entered=true;
			new Thread(
					new Runnable(){
						public void run() {
							while(entered){
								tmpPoint = getPossition(spielFeld.getMousePosition());
								if(  lastTmpPoint.x != 0 && lastTmpPoint.y != 0 && lastTmpPoint.x != tmpPoint.x && lastTmpPoint.y != tmpPoint.y && !field[lastTmpPoint.x][lastTmpPoint.y] ){
									setPoint(lastTmpPoint, 2);
								}
								if(  tmpPoint.x != 0 && tmpPoint.y != 0 && !field[tmpPoint.x][tmpPoint.y] ){
									setPoint(tmpPoint, 1);
								}
								lastTmpPoint = tmpPoint;
								tmpPoint = null;
								try {
									Thread.sleep(80);
								} catch (InterruptedException e) {
									e.printStackTrace();
								}
							}
							
						}
						
					}
			).start();
		}

		public void mouseExited(MouseEvent arg0) {
			entered = false;
			
		}

		public void mousePressed(MouseEvent arg0) {
			
		}

		public void mouseReleased(MouseEvent arg0) {
			tmpPoint = getPossition(spielFeld.getMousePosition());
			if(  tmpPoint.x != 0 && tmpPoint.y != 0 && !field[tmpPoint.x][tmpPoint.y] ){
				setPoint(tmpPoint, 3);
			}
			
		}
    	
    }
	
    private Point getPossition(Point p){
    	Point opticalP = new Point();
    	float opticalX = (float)(p.x+radius)/(float)gridsize;
    	float opticalY = (float)(p.y+radius)/(float)gridsize;
    	if( opticalX <= Float.valueOf(new String((int) opticalX+".3"))  &&  opticalY <= Float.valueOf(new String((int) opticalY+".3"))){
    		opticalP.setLocation((int)opticalX, (int)opticalY);
    	}
    	
    	return opticalP;
    }
 
sorry, dass der code etwas caothisch ist, werde sobald das hier funktioniert mal schauen, dass ich da aufräume und den sauberer hinschreibe.

zu deiner frage, hier paar var deklarationen:
Java:
    private boolean entered = false;
	private Graphics gi;
	private BufferedImage background;
	private Point tmpPoint, lastTmpPoint = new  Point(0,0);
	private OpticalPane spielFeld;
	private int gridsize = 61;
    private int radius = 9;
	private boolean[][] field;
	private boolean[][]  redField;
	private boolean[][]  blueField;
	private String player1=null, player2 = null;
	private int yourColor  = 1;
	private int size;
 
Statt der drei Arrays field, redField, blueField würde ich den Zustand des Spielfelds in einem (z.B. int) Array speichern, das erspart Dir z.B. beim Zeichnen die if-Abfragen.
So wie ich das sehe, zeichnest Du immer alles auf ein BufferedImage, um dieses dann auf die Komponente zu malen. Warum malst Du nicht direkt auf die Komponente? Das Spielfeld selbst kann man ja als Image vorhalten, aber zumindest die Punkte (da diese ja Ihren Zustand verändern können) würde ich direkt in der paintComponent() zeichnen.
 
Dazu gibt's hier im Forum jede Menge Threads. Alles, was auf den Bildschirm gezeichnet wird, muss von der paintComponent-Methode aus gezeichnet werden. Um zu sagen, wie man das im konkreten Fall machen könnte, wurden bisher zu chaotische und zu wenig zusammenhängende Codestücke gepostet.
 

Neue Themen


Zurück
Oben