Aliasing-Effekte? (Blinken beim schnellem Zeichnen)

Status
Nicht offen für weitere Antworten.

hdi

Top Contributor
hey,

und zwar dachte ich, ich hab in dem zusammenhang das wort "aliasing" mal gehört.
es geht darum, dass ich ein Frame hab wo sich ein wurm hindurchschlängelt (wie in snake).

dieses bewegen des wurms funktioniert in meinem programm so:

alle linien des alten wurms werden gelöscht, es werden die neuen berechnet, je nachdem wohin der wurm sich bewegt, und dann gezeichnet.

allerdings flimmert das krass. sprich man sieht für ein paar millisekunden den moment zwischen
dem löschen und dem neuzeichnen, also ein leeres Frame.
ich glaube irgendwo eben mal etwas dazu gelesen zu haben.

es bringt auch nix, wenn ich das löschen erst kurz vor dem neuzeichnen mache.

Wie kriegt man das in den Griff?

Falls ihr noch paar Infos benötigt:

benutze ein JFrame, die Grafiken sidn wie gesagt Linien, und zwar durch eine kleine Hilfsunktion die alle Punkte zwischen zwei Punkten (= Linie) ausmalt.

Wär dankbar für Hilfe! Bin so stolz dass ich nap jetzt ein funktionierendes snake programmiert hab, aber es sieht halt einfach sch** aus mit diesem flimmern :bloed:
 
E

exi

Gast
Hallo hdi,

eigentlich sollte Java von sich aus das DoubleBuffering unterstützen. Aber irgendwie flimmert es dann doch immer irgendwo weil Java schneller präsentiert als zeichnet.
Als kleine Abhilfe kannst du diese Technik aber simulieren. Du arbeitest mit zwei BufferedImages. Während eines, nennen wir es bild[0], angezeigt wird kannst du bild[1] modifizieren. Wenn du damit fertig bist übergibst du bild[1] an dein JFrame und modifizierst bild[0] im Hintergrund.
Falls deine Bilder nicht zu viele Details enthalten wird bild schnell angezeigt. Letztlich präsentierst du damit immer nur statische Ansichten. Aber die dauernde Abfolge der Bilder bringt auch eine Dynamik ins Spiel.
Und, falls du irritiert sein solltest: früher, in der Zeit der prozeduralen Programmierung, konnte man nur derart arbeiten. Man hielt im 'Heap' zwei Speicherbereiche welche die Graphikkarte (eigentlich deren Inhalt) wiederspiegelten. Das eine schob man auf die Graphikkarte, das andere wurde nach Bedarf gesetzt. Und dann umgekehrt.

tschüs
exi

hdi hat gesagt.:
hey,

und zwar dachte ich, ich hab in dem zusammenhang das wort "aliasing" mal gehört.
es geht darum, dass ich ein Frame hab wo sich ein wurm hindurchschlängelt (wie in snake).

dieses bewegen des wurms funktioniert in meinem programm so:

alle linien des alten wurms werden gelöscht, es werden die neuen berechnet, je nachdem wohin der wurm sich bewegt, und dann gezeichnet.

allerdings flimmert das krass. sprich man sieht für ein paar millisekunden den moment zwischen
dem löschen und dem neuzeichnen, also ein leeres Frame.
ich glaube irgendwo eben mal etwas dazu gelesen zu haben.

es bringt auch nix, wenn ich das löschen erst kurz vor dem neuzeichnen mache.

Wie kriegt man das in den Griff?

Falls ihr noch paar Infos benötigt:

benutze ein JFrame, die Grafiken sidn wie gesagt Linien, und zwar durch eine kleine Hilfsunktion die alle Punkte zwischen zwei Punkten (= Linie) ausmalt.

Wär dankbar für Hilfe! Bin so stolz dass ich nap jetzt ein funktionierendes snake programmiert hab, aber es sieht halt einfach sch** aus mit diesem flimmern :bloed:
 

The_S

Top Contributor
Ich glaube nicht, dass das jemand, der noch nie etwas davon gehört hat, auch verstanden hat ;) . Ein kleines Codebeispiel:

Code:
public class YourClass extends JFrame {

   private BufferedImage dbuffer = null;

   public void initDBuffer(int width, int height) { // Größe des Zeichenbereichs

      dbuffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
   }

   public void paint(Graphics g) {

      Graphics2D g2d = dbuffer.createGraphics();
      g2d.setColor(Color.black);
      g2d.fillRect(0, 0, dbuffer.getWidth(), dbuffer.getHeight());
      g2d.setColor(Color.white);
      g2d.drawRect(50, 50, 100, 100);
      // Deine Zeichenoperationen werden jetzt auf g2d anstelle von g angewand
      g2d.dispose();
      g.drawImage(dbuffer, 0, 0, this);
   }
}
 
G

Guest

Gast
hey, danke erstmal.

uff, das überfordert mich gerade. (java is genial, aber wenn man neue sachen lernt finde ich ist es immer sehr anstrengend. für jeden dreck 400 klassen und funktionen...)

Ich kenn alle diese Klassen nicht und hab null ahnung wie die vererbungs-technisch zusammenhängen.

Ich müsste meine komplette Ansicht eh neu machen, weil ich im Moment eine Klasse meines Profs verwende, und sie überhaupt gar nicht verstehe ^^

Ich bräuchte dabei aber bitte eure Hilfe, ich komm wie gesagt mit dem Klassen-Wirrwarr nicht zurecht und weiss nicht, wie ich das strukturieren soll..

Ich geb euch mal den groben Aufbau meines Programms, ist nicht viel:

Ich hab den Wurm in der Klasse "Worm". So ein Wurm hat n paar funktionen, die hier egal sind, und besteht aus einem Array von Zellen.

Zellen ist eine andere, eigene Klasse. Sie erbt von Rectangle. Eigentlich ist sie auch ein Rectangle, ich hab sie fast null geändert (ich hab nur 1 oder 2 Attribute mehr, die Rectangle nicht hat - die sind aber für die Ansicht egal).
Also einfach gesagt: Zelle = Rectangle.

So, und ich muss mir jetzt eben ne Ansichts-Klasse basteln, die einen Wurm mit Anti-Aliasing bewegt.

Wenn sich ein Wurm bewegt (er hat dafür eine Methode "move()"), dann soll in der ansichts-klasse irgendwie folgendes passieren:

Für jede Zelle im Wurm, (was letztendlich heisst: für die Position des ganzen Wurmes) sollen alle Linien, die diese
Zelle umfassen (die Klasse Rectangle hat ja einen awt.Point, und mit der länge/breite lassen sich ja auch die anderen Eckpunkte bestimmen), in der Farbe weiss gezeichnet werden (alte position des wurms löschen).
Dann wird die neue Position errechnet, was ja mit der Ansich nix zu tun hat.
Dann ekommt die Ansicht einen neuen Wurm, und soll diesmal das selbe Verfahren machen, nur nicht in der Farbe weiss, sondern schwarz, also ihn sichtbar machen.

Okay, und genau dieser komlpette Prozess muss gepuffert werden.
Und dafür brauch ich jetzt eine Klasse, die halt meine verwendeten Klassen berücksichtigt! =)

(die funktion unten paint() z.B. will ein "graphics" haben, das ist ja mit meiner Programmstruktur nicht kompatibel)

Zusammengefasst: Die Ansichts-Klasse soll mehrere Rectangles gepuffert zeichnen.

Könnt ihr mir dafür bitte einen Grundriss geben? Welche Klasse soll ich benutzen? JFrame? Welche Klassen brauch ich genau für die Pufferung, wie würde dann konkret die Funktion "move" des wurmes aussehen? (natürlich sind nur die Aspekte gemeint, die die Zeichnung betreffen)

Sorry - viel Text und viel Forderungen :p Ich finde aber wie gesagt wirklich, dass man als Anfänger in Java zu krass untergeht. Ist ja toll was es alles gibt, aber es überflutet einen und die Zusammenhänge der Klasse, welche jetzt von welcher extends und welche was imlpements etc...
Ich weiss grad echt nicht, wie meine Ansichts-Klasse aussehen soll :(
 

Illuvatar

Top Contributor
Kleine Anmerkung am Rande: "Aliasing" meint etwas anderes, nämlich, dass Linien auf einer "pixeligen" Darstellungsfläche normalerweise erstmal "stufig" dargestellt werden. Durch Anti-Aliasing werden noch weitere Pixel in der Nähe der Linie gefärbt, wodurch ein besseres Aussehen der Linie erreicht wird.
 

hdi

Top Contributor
Nochmal wegen der paint() Methode:

Ich hab in der API bei der Klasse Graphics nachgeschaut, da steht "direct known sublasses": Graphics2D

da wiederum stehen keine Subclasses.

Und ich habe ja Rectangles, die ich zeichnen möchte?! Also hilft mir paintComponent() auch nicht weiter, Rectangle ist ja ein awt-Element..

ich verstehe nicht so ganz... Wie kann ich ein Rectangle zeichnen? So wie ich jetzt eben die API von Graphics verstanden hab, wird das da nicht unterstüzt?
 

Ariol

Top Contributor
Vielleicht hab ich ja was falsch verstanden, aber bei mir flackert da nix.

Code:
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Rectangle;

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

public class SnakeTest extends JFrame
{

	SnakePanel	snakePanel;

	public SnakeTest()
	{
		setLayout(new FlowLayout());

		snakePanel = new SnakePanel();
		add(snakePanel);

		pack();
		setVisible(true);

		setDefaultCloseOperation(EXIT_ON_CLOSE);
	}

	public static void main(String[] args)
	{
		SnakeTest snakeTest = new SnakeTest();
	}

	class SnakePanel extends JPanel
	{

		MovingRectangle[]	rectangles	= new MovingRectangle[20];

		public SnakePanel()
		{
			setPreferredSize(new Dimension(200, 200));

			for (int i = 0; i < rectangles.length; i++)
			{
				rectangles[i] = new MovingRectangle(this, 0, 0, rectangles.length+1-i, rectangles.length+1-i,  (rectangles.length+2-i)/2, (i+1)*30);
			}

			SwingUtilities.invokeLater(new Runnable()
			{
				public void run()
				{
					for (MovingRectangle rectangle : rectangles)
					{
						rectangle.startMove();
						try
						{
							Thread.sleep(110);
						}
						catch (InterruptedException e)
						{
						}
					}
				}
			});
		}

		@Override
		public void paint(Graphics g)
		{
			g.clearRect(0, 0, getWidth(), getHeight());
			for (MovingRectangle rectangle : rectangles)
			{
				g.drawRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
			}
		}
	}

	class MovingRectangle extends Rectangle
	{
		private SnakePanel	owner;

		private int			step;

		private long		sleep;

		Thread				move	= new Thread()
									{
										public void run()
										{
											while (true)
											{
												int x = getLocation().x;
												int y = getLocation().y;
												int width = getSize().width;
												int height = getSize().height;
												int ownerWidth = owner.getWidth();
												int ownerHeight = owner.getHeight();

												if (x < ownerWidth - width && y <= 0)
												{
													x += step;
												}
												else if (x >= ownerWidth - width && y < ownerHeight - height)
												{
													y += step;
												}
												else if (x > 0 && y >= ownerHeight - height)
												{
													x -= step;
												}
												else if (x <= 0 && y > 0)
												{
													y -= step;
												}

												try
												{
													sleep(sleep);
												}
												catch (InterruptedException e)
												{
												}

												setLocation(x, y);
												owner.repaint();
											}
										};
									};

		public MovingRectangle(SnakePanel owner, int x, int y, int width, int height, int step, long sleep)
		{
			super(x, y, width, height);
			this.owner = owner;
			this.step = step;
			this.sleep = sleep;
		}

		public synchronized void startMove()
		{
			move.start();
		}

	}
}
 

hdi

Top Contributor
:shock: :shock: :shock:

okeee... also erstmal :applaus: und :toll: und :D

werd mir das mal genau anschauen, und versuchen auf mein Programm zu übertragen..
Aber ich könnte wetten, ich meld mich nochma ;)

Auf jeden Fall schon mal tausend Dank dafür, konkrete Code-Beispiele, die funktionierende Programme sind, sind halt immer das beste, da kann man dann Schritt für Schritt was ändern und ausführen, dann checkt man genau, was jetzt wofür da ist...

Also bis später :bae:
 

Wildcard

Top Contributor
@Ariol
gut gemeint, aber leider daneben.
Ein paar Sachen die mir sofort auffallen:
1. nicht paint überschreiben, sondern paintComponent (und dort super aufrufen)
2.
Code:
         SwingUtilities.invokeLater(new Runnable()
         {
            public void run()
            {
               for (MovingRectangle rectangle : rectangles)
               {
                  rectangle.startMove();
                  try
                  {
                     Thread.sleep(110);
                  }
                  catch (InterruptedException e)
                  {
                  }
               }
            }
Nicht den Event Dispatch Thread schlafen legen!

3.
Code:
                                    if (x < ownerWidth - width && y <= 0)
                                    {
                                       x += step;
                                    }
                                    else if (x >= ownerWidth - width && y < ownerHeight - height)
                                    {
                                       y += step;
                                    }
                                    else if (x > 0 && y >= ownerHeight - height)
                                    {
                                       x -= step;
                                    }
                                    else if (x <= 0 && y > 0)
                                    {
                                       y -= step;
                                    }
Nimm die Methoden von Rectangle um solche Dinge zu prüfen.

4.
Code:
                                    setLocation(x, y);
                                    owner.repaint();
Das setLocation hat einen indirekten Einfluss auf die GUI. Du befindest dich aber nicht im Event Dispatch Thread, das Verhalten in dieser Situation ist nicht definiert. Das Resultat sind sehr schwer zu findende Fehler.

5.
Code:
      public synchronized void startMove()
      {
         move.start();
      }

Welchen Sinn verfolgt das synchronized?

6. Viel zu viele Threads! Benutze einen Thread der alle Objekte bewegt.
 
G

Guest

Gast
okay, bin von meiner Analyse zurück.

Ergebnis: erschütternd! :bahnhof:

Das Programm enthält sehr viele Komponenten, von denen ich noch nie etwas gehört habe.
z.B. Threads, synchronzied, und alle möglichen Funktionsaufrufe, z.B. die von SwingUtility..

Also ich bemühe mich ja echt, das auch meine Anwendung adaptieren zu können, aber ich schätze mir fehlt ein fundamentaler Überblick, und so lauf ich dauernd ins Leere...

Mit der Info, dass ich es durchaus versucht habe, aber nicht kapiere, lass ich euch jetzt einfach auf mein Programm los. Ich habe alles spieltechnische weggelassen, es geht hier nur um den Zusammenhang der Klassen.
Ihr könnt davon ausgehen, dass alle Funktionen Zugriff auf das haben, was sie übergeben bekommen.
So kann z.B. jede Funktion in der Klasse "View" auf alle Attribute einer Zelle oder eines Wurms zugreifen.
(Sprich ich hab getter und setter hier mal weggelassen)

Code:
public class Cell extends Rectangle{
	
	public Cell(int x, int y,){
		super(x,y,Data.CELL_SIZE,Data.CELL_SIZE);
	}
}

Code:
public class Worm{
	
	private Cell[] body;
	private int length;
	
	public int getLength(){
		return length;
	}
	
	public Cell[] getBody(){
		return body;
	}
	
	
	/* CONSTRUCTOR */

	public Worm(){
		body = new Cell[Data.MAX_CELLS];
		body[0] = new Cell(50,0);
		body[1] = new Cell(50,5);
		body[2] = new Cell(50,10);
		length = 3;
		isAlive = true;
	}
}

Code:
public class View extends JFrame{
	
	private BufferedImage dbuffer;
	private JPanel panel;   // Das soll also der Bereich sein, wo das Spiel läuft, also wo gezeichnet werden soll
        private JLabel label;    // Das is oben am Fenster ein kleiner Bereich, wo Punktestand etc. stehen
	
	public void drawCell(Cell pre, Cell post){
                              // ???
                              /* folgendermassen sollte das ablaufen :
                              er bekommt ne Zelle "pre", malt die Linien davon weiss aus, 
                              malt dann die Linien der Zelle "post" schwarz aus.
                              Muss gepuffert ablaufen eben...*/
	}
	
	public void drawWorm(Worm pre, Worm post){
                             // ????
                            /* das gleiche Spiel, nur das ein Wurm eben aus vielen Zellen besteht 
                            Auch hier muss es gepuffert sein, damit es eben nicht so komisch flackert. 
                            Das heisst, dass der KOMPLETTE wurm gepuffert gemalt werden muss, und 
                           erst auf dem panel gezeichnet wird, wenn alles fertig ist, oder wie auch immer 
                           das mit der Pufferung läuft ...*/
		}
	}
	
	/* CONSTRUCTOR */
	
	public View(){
		super("mein Fenster");
		setLayout(new FlowLayout());
		setVisible(true);
		
		dbuffer = new BufferedImage(Data.HOR_RES, Data.VER_RES, BufferedImage.TYPE_INT_RGB); 
		panel = new JPanel();
		add(panel);
	}
}

Okay, also wie gesagt, ich les ja auch fleißig in der API usw, aber hab halt echt kein Boden unter den Füßen bei dieser ganzen Sache...Einfach zu viele Klassen, von denen ich noch nie was gehört hab..

Danke :roll:
 

Ariol

Top Contributor
Hobbit_Im_Blutrausch hat gesagt.:
@Ariol bei Fenstern (z. B. JFrame) und AWT paint, beim restlichen Swing paintComponent ;) .

Ich weiß, ich weiß...

Stand auch schon da, fiel aber dann Testzwecken zum Opfer. Es ruhe in Frieden.
 

Ariol

Top Contributor
Ok, ok...war ja nur schnell zusammengeschustert...

1. - s.o.
2. - nicht drauf geachtet.
3. - versteh nicht welche Methoden du damit meinst!?
4. - diese Methode setzt nur x und y - wo liegt das Problem?
5. - stammt noch aus einer früheren Version.
6. - das war absolute Absicht. Schließlich ging es ja um's Flimmern. So bekommt man eine Art WorstCase
 

Wildcard

Top Contributor
3. intersects, contains,...
4. Das Problem liegt darin, das Swing nicht threadsicher ist. Wenn die Daten manipulierst die beim Zeichnen verwendet werden, muss das mit dem EDT synchronisiert werden.
6. Dann solltest du aber vielleicht dazuschreiben das es sich um eine Art Worst-Case Szenario handelt, hinterher baut er/sie noch sowas ein :?
 

Ariol

Top Contributor
Danke für die Antworten.
Es ging nur ums aufzeigen, dass sich Rectangles zeichnen lassen und dass bei mir nicht flackert.
 
G

Guest

Gast
macht mal den ariol nich so runter ;)

ne also, ich wäre noch immer dankbar für Code-SChnipsel die speziell für mein Programm angepasst sind, da ich aus den Beispielen bisher nicht extrahieren kann, was für meine Anwendung wichtig ist, und was Teil des Beispiels war und für meine Anwendung gar nicht wichtig ist.

Mein Hauptproblem ist, dass ich nicht weiss, was die Pufferung umfassen soll..

z.B. bei dem Zeichnen des ganzen Wurms, da muss doch der komplette Wurm gepuffert gezeichnet werden, und nicht Zelle für Zelle, oder? Denn genau in letzterem Fall hat man ja dieses Flackern.

Und ich weiss eben nicht was ich in diesen Puffer-Rahmen hineinpacken soll. Bitte gebt mir ein Beispiel der Funktion drawWorm() aus meinem Code, die das alles gepuffert zeichnet.

Danke :)
 

Wildcard

Top Contributor
Wenn du einen JFrame hast, brauchst du keine separate Pufferung. Wenn etwas flackert, ist der Fehler vermutlich an anderer Stelle.
 

hdi

Top Contributor
Wenn du einen JFrame hast, brauchst du keine separate Pufferung. Wenn etwas flackert, ist der Fehler vermutlich an anderer Stelle.

ahhh, und genau diese Stelle hab ich nun gefunden !
hab jetzt nochmal von vorne begonnen, und konnte wieder wunderbar zeichnen.

das problem des "flackerns" ist kein puffer-problem, sondern nur von mir als programmierer doof gemacht.
das flackern ist nichts anderes als linien, die weiss ausgemalt und sofort wieder blau ausgemalt werden.
dass man das sehen soll, ist vom JFrame ja auch richtig gelöst so.

Natürlich ist es aber nicht das, was ich wollte.

Und nun stehe ich vor dem Problem, das ich benennen kann, aber nicht lösen :autsch:

Wie würdet ihr das machen, hier noch das Szenario:

Ein wurm besteht aus vielen Linien, wenn er sich bewegt sollen natürlich nicht nur die Linien an seiner neuen Position gezeichnet werden, sondern die alten auch gelöscht werden (heisst: weiss gezeichnet werden).
Dabei ensteht eben im Moment das Flackern, weil er Linie für Linie zeichnet.

Und das ist das Problem.. Ich scheine die Abfolge der Befehle drawWorm und rubbishWorm irgendwie in einer doofen Reihenfolge gewählt zu haben, bzw. ihre Implementierung ist so gemacht, dass es nur flackern kann..

Boah - wie mach ich das jetz?

Wie würdert ihr z.B. 3 aneinanderhängende Rechtecke durch das Fenster bewegen lassen, also zeichentechnisch?
Da kommt halt zwangsweise dieses Flackern bei raus, weil wenn z.B. das erste Rechteck gelöscht wird (weil es eine neue Position bekommen hat), dann wird es weiss gezeichnet, allerdings zieht quasi gleichzeitig (paar ms/ns danach) das Nachbarrechteck nach und malt sich blau aus.

Dann flackert es blau (schnelle Abwechslung von weiss und blau gezeichneten linien).

Aber wie lös ich sowas oO?

EDIT:

okay bin schon ein schritt weiter: ich muss ja nicht immer den komlpetten wurm weiss ausmalen, und dann neu in blau zeichnen! da der wurm ja lückenlos ist und aus aneinandergereihten zellen besteht, reicht es ja nur jeweils da, wo die letzte zelle im wurm vorbeikommt, weiss zu zeichnen.
also ich meine wenn der wurm z.B. aus 20 zellen besteht, dann muss ich ja nicht die alte position der ersten zelle nach einer bewegung weiss malen, denn genau dorthin kommt eh sofort die zweite zelle des wurms und würde sie wieder blau malen, usw.

Aber selbst dieses einmalige zeichnen einer einzigen linie bei der letzten zelle des wurms bringt das ende des wurms ins flackern (also seine letzte zelle).

ich schätze ich brauche für diese eine zelle eine spezielle draw-funktion, und zwar dass nicht alle 4 linien des rechtecks gezeichnet werden, sondern nur 3 (und die, die sich die linie mit der letzten zelle teilt, nicht)

also ich meine zwei zellen teilen sich ja eine linie, wenn sie direkt aneinanderhängen.
diese darf auch nicht weiss gezeichnet werden, weil die letzte zelle diese sofort wieder blau malt. wäre also wieder ein flackern.

hm.. okay ich versuch mich mal dran.
wobei ich mich noch immer frag, wie ariol das bei sich gemacht hat...sicherlich nicht so komlpiziert. aber aus seinem quellcode werde ich leider nich schlau :(
 

Wildcard

Top Contributor
Geh von folgender Annahme aus:
Du weißt nicht wann gezeichnet wird und es ist nicht dein Problem.
Wenn gezeichnet wird, zeichnest du den aktuellen Zustand deines Datenmodells

That's it. Mehr ist an dieser Stelle nicht zu tun.
Swing löscht das alte Bild bevor das neue gezeichnet wird, auch darum brauchst du dich also nicht zu kümmern.
 

hdi

Top Contributor
@wildcard:

also ich weiss ja nicht was du meinst, aber meiner meinung nach ist das sogar ein verdammt großes problem für mich!
siehe update in meinem post weiter oben.

ich weiss nicht, ob ihr mit meinen beschreibungen euch vorstellen könnt, wie meine klassen aussehen, aber ich meine dieses zeichnen kommt ja nicht von irgendwoher. das muss ich ja selber alles, punkt für punkt, malen lassen.

und wenn man sehr schnell die farben zwischen blau und weiss wechselt bei einer linie, sieht das halt aus wie flackern.
das macht meine draw-funktion leider nicht für mich, ich versteh nich so ganz was du meinst :p
 

Wildcard

Top Contributor
Du sollst gar nichts übermalen!
Angenommen du weißt das dein Wurm an Position p1,p2,p3,p4 und p5 liegt, dann muss deine paint(Component) Methode ein Rechteck an p1,p2,p3,p4 und p5 zeichnen und mehr nicht
 
G

Guest

Gast
okay, du gehst wohl davon aus, dass ich das mit den richtigen klassen und funktionen mache :p
leider bin ich totaler anfänger, und ich tue das wohl nicht.

denn MEINE methode zum zeichnen malt etwas bestimmtes ins koordinatensystem.
wenn ich nun etwas neues woanders hin male, ist das, was ich vorher gemalt habe, noch immer da!

du allerdings scheinst zu denken, dass das nicht der fall ist.
also meine methode...sie zeigt kein panel an mit der grafik, wo der wurm grad ist.
sondern sie fügt meinem panel die grafik hinzu.

wenn ich nichts übermalen würde, wäre das komlpette fenster nach 1 minuten spielen einfach nur blau gefüllt, weil der wurm überall schon mal war..

das große problem ist: ich kann dir nicht mal so genau sagen, was meine methode zum zeichnen macht, weil
ich, wie schon mal erwähnt, eine klasse von meinem prof verwende. da gibts ne methode draw(), und die zeichnet einen punkt wohin, sowie ein drawLine() die eine Linie zwischen zwei Punkten zeichnet.

wenn ich drawLine() aber 100 mal ausführe, dann hab ich am schluss 100 linien...deswegen muss ich das immer wieder übermalen.

ich hoffe du verstehst, was ich meine..
was soll ich denn benutzen, wie soll ich das zeichnen? ich kann keine eigene mal-methode schreiben, ich habs versucht aber ich kenne alle packages und klasen etc dafür nicht..

sorry, ich glaube ihr schätzt mich falsch ein.
Ich habe GAR KEINE ahnung, wie man etwas in ein fenster zeichnet!!
ich benutze nur diese dumme methode meines professors, und ich verstehe sie nicht im geringsten!

also, wie sieht denn die draw-methode aus, die dir so im kopf schwebt bei dem, was du sagst?
 

Wildcard

Top Contributor
Dann gib uns mal die Klasse deines Professors.
Möglicherweise ist selbige einfach grob falsch implementiert. :?
 

Ariol

Top Contributor
Wenn du paintComponent() benutzt schreib mal als erstes in diese Methode
Code:
 super.paintComponent(g);

Das löscht dein vorheriges Bild.
 
G

Guest

Gast
okay,
ich denke mein prof kann das schon ganz gut, macht zumindest so den eindruck.
ich schätze, ich benutze das alles nur falsch..

hier ist die klasse, nich erschrecken. sie ist ziemlich lang.
aber das liegt daran, dass sie auch n mouseListener und einige andre dinge hat.
ich will aber erstmal nix rausnehmen, weil ich check den zusammenhang nich.

Code:
import java.util.ArrayList;
import javax.swing.JFrame;
import java.io.*;
import javax.swing.JPanel;
import java.awt.event.MouseAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.RectangularShape;
import java.awt.geom.GeneralPath;
import java.awt.Point;
import java.awt.Label;
import java.awt.Font;
import java.awt.Color;

/**
   Eine Klasse zu p�dagogischen Zwecken.  
   Erlaubt die Eingabe von Punktkoordinaten
   mittels Mausklicks, das Zeichnen einfacher 
   2D Objekte (java.awt.Shape), sowie die 
   Ausgabe von Texten in einer Statuszeile.  
   @version 2.0
*/

public class GraphicsWindow extends JFrame {
  
    private static int fensterZahl;
    private static int fensterNr;
    private Label label;
    private GraphicsWindowPanel panel;
    private Point mousePos;
    private Object lock;
    MyMouseAdapter mouseListener; 

    /**
       Erzeugt ein Fenster mit Textausgabe, Mauseingabe und Grafikausgabe.
    */
    public GraphicsWindow() {
	super();
	setTitle("Grafikfenster " + ++fensterNr);
	fensterZahl++;
	setSize(640,480);
	
	addWindowListener(new WindowAdapter(){
		public void windowClosing(WindowEvent e) {
		    dispose(); // nicht gleich alle Fenster abschiessen
		    if (--fensterZahl<1) System.exit(0);
		}
	    });

	label = new Label("Statuszeile...");
	label.setFont(new Font("Helvetica", Font.PLAIN, 12));
	getContentPane().add(label,"North" );
	
	panel = new GraphicsWindowPanel();
	//panel.setBackground(Color.cyan);
	getContentPane().add(panel,"Center");
	mousePos = new Point();
	mouseListener = new MyMouseAdapter();
	panel.addMouseListener(mouseListener);
	setVisible(true);
    }

    /**
       Gibt eine Zeichenkette oben im Fenster aus.
       @param text diese Zeichenkette
    */
    public void setText(String text) {
	label.setText(text);
    }
    /**
       Liest den oben im Fenster angezeigten Text aus.
       @return den Text
    */ 
    public String getText() {
	return label.getText();
    }
    /**
       Wartet auf einen Mausklick. Die Methode blockiert das
       aufrufende Programm solange bis der Mausklick erfolgt ist.
       @return die Koordinaten des angeklickten Punkts
    */
    
    public Point mouseClick() {
	try{ 
	    synchronized(mouseListener){mouseListener.wait();}
	}
	catch(InterruptedException e){
	    e.printStackTrace();
	}
	return mousePos;
    }

    class MyMouseAdapter extends MouseAdapter {
	
	/**
	   Beendet das Warten auf den Mausklick und verwertet die Koordinaten.
	   Diese Methode ist nicht f�r den Anwender bestimmt.
	*/
	
       synchronized public void mouseClicked(MouseEvent e){
	   mousePos = e.getPoint();
	   notifyAll();
       }
    }


    /**
       Schaltet die Zeichenfarbe auf die Hintergrundfarbe um. Dies ist
       das Mittel, um gezeichnete Linien wieder zu l�schen.
    */
    public void switchToBackgroundColor(){
	panel.addCommand(new SwitchToBackgroundColor());
	panel.repaint();
    }

    /**
       Schaltet die Zeichenfarbe auf Schwarz um.
    */
    public void switchToForegroundColor(){
	panel.addCommand(new SwitchToForegroundColor());
	panel.repaint();
    }

    /** Liefert die aktuelle Zeichenfarbe. 
	@return die aktuelle Zeichenfarbe des GraphicsWindow. */
    public Color getColor() {
	return panel.getGraphics().getColor();
    }
    /**
       Zeichnet eine Linie in der aktuellen Zeichenfarbe. 
       @param x Anfangspunkt
       @param y Endpunkt
    */
    public void drawLine(Point x, Point y){
	panel.addCommand(new DrawLine(x,y));
	panel.repaint();
    }

    /** 
	Zeichnet ein geometrisches Objekt. 
    */
    public void draw(Shape s) {
	panel.addCommand(new Draw(s));
	panel.repaint();
    }
    /** 
	Fuellt ein geometrisches Objekt aus. 
    */
    public void fill(Shape s) {
	panel.addCommand(new Fill(s));
	panel.repaint();
    }

    /** Das aufrufende Programm wird fuer ein gegebene Zeitspanne blockiert.
	@param millis Die Zeitspanne in Millisekunden*/
    public void sleep(long millis) {
	try {Thread.sleep(millis);} catch (Exception e){}
    }

    /** Setzt die Zeichenfarbe */
    public void setColor(Color d) {
	panel.addCommand(new SetColor(d));
	panel.repaint();
    }
    /**
     * Testet, ob Punkt p im zeichenbaren Bereich liegt.
     * @param p der zu testende Punkt.
     * */
    public boolean includes(Point p){
    	return panel.contains(p);
    }


}

class GraphicsWindowPanel extends JPanel
{
    private ArrayList<Command> cl = new ArrayList<Command>();

    private int num = 0;

    public void paintComponent(Graphics g)
    {
	super.paintComponent(g);
	Graphics2D g2D = (Graphics2D)g;
	// System.out.println("Called paintComponent()" + (num++));

	for (int i=0; i<cl.size(); i++) {
	    cl.get(i).execute(g2D);
	}
    }

     void addCommand(Command c)
    {
	cl.add(c);
    }
}


abstract class Command implements Serializable
{
    abstract  void execute(Graphics2D g2D);

    /** Clone a shape. This method is needed because Shape
     * does not define clone(), although many shape classes do.
     * Kopiert aus jsky-2.6 auf ftp.eso.org */
    static Shape cloneShape(Shape s) {
        // FIXME Add more specific shapes
        if (s instanceof RectangularShape) {
            return (RectangularShape) ((RectangularShape) s).clone();
        } else {
            return new GeneralPath(s);
        }
    }
}

class DrawLine extends Command {
    Point von;
    Point bis;
    DrawLine(Point von, Point bis) {
	/* Clonen der Punkte essentiell um Aliasingeffekte beim Redraw zu verhindern */
	this.von = new Point(von);
	this.bis = new Point(bis);
    }
     void execute(Graphics2D g2D) 
    {
	g2D.drawLine(this.von.x,this.von.y,this.bis.x,this.bis.y);
    }
}

class SwitchToForegroundColor extends Command {
    SwitchToForegroundColor() {}
     void execute(Graphics2D g2D) {
	    g2D.setColor(Color.black);
    }
}

class SwitchToBackgroundColor extends Command {
    SwitchToBackgroundColor() {}
     void execute(Graphics2D g2D) {
	g2D.setColor(g2D.getBackground());
    }
}

class SetColor extends Command {
    Color color;
    SetColor(Color color) {this.color = color;}
     void execute(Graphics2D g2D) {
	g2D.setColor(this.color);
    }
}


class Draw extends Command {
    Shape shape;
    Draw(Shape shape) {this.shape = cloneShape(shape);}
     void execute(Graphics2D g2D) {
	g2D.draw(this.shape);
    }
}

class Fill extends Command {
    Shape shape;
    Fill(Shape shape) {this.shape = cloneShape(shape);}
     void execute(Graphics2D g2D) {
	g2D.fill(this.shape);
    }
}
 

Wildcard

Top Contributor
Nun, ist zwar ein lustiges Konstrukt, aber für ein Snake Spiel nicht benutzbar, da du einmal abgesetzte Commands nicht mehr löschen kannst, du also tatsächlich jedes einzelne Element überzeichnen müsstest.
War das etwa eine Aufgabe damit ein Spiel zu machen? Das kann ich mir nicht vorstellen.
 
G

Guest

Gast
wie gesagt, es ist wohl eine falsche Anwendung von mir.
Es war nicht Aufgabe, damit ein Snake Spiel zu machen.

Ich habe sie nur benutzt, weil ich nicht weiss, wie man etwas zeichnet :oops:
ich kann das halt alles nicht..

Deswegen war ja auch die Frage, ob ihr mir eine Vorlage für so etwas geben könnt.
Lieder konnte ich mit Ariol's Biespiel nichts anfangen, weil auch hier viele Sachen gar nicht
für mein Spiel gedacht waren. Ich kann aber nicht erkennen, was für mich wichtig ist, und was nicht.

Ich will ja schon lange eine eigene Ansichts-Klasse machen, aber hab keine Ahnung wie.

Nun, du kennst ja jetzt mein Spiel, und mein Konzept. Sprich ein Wurm ist ein Array von Zellen (was auch immer das sind, ob Rechtecke, Kreise etc, eigentlich würde ich lieber Kreise nehmen als Rechtecke).

und immer wenn er sich irgendwohin bewegt akutalisiert er seine Zellen. Die muss ich dann alle malen.

Wie kann ich so eine klasse erstellen, von der du sprichst? Wo ich nix übermalen muss? Der ich nur nen Wurm geb, und die zeichnet ihn, aber löscht auch den alten Wurm?
 

Wildcard

Top Contributor
Du musst es definitiv anders machen, denn mit diesen Klassen lässt sich auf keinen Fall ein sich bewegendes Bild realisieren (zumindest nicht ohne massives flimmern und viel zu viel Arbeit)

Wie kann ich so eine klasse erstellen, von der du sprichst? Wo ich nix übermalen muss? Der ich nur nen Wurm geb, und die zeichnet ihn, aber löscht auch den alten Wurm?
Genau das passiert hier:
Code:
    public void paintComponent(Graphics g)
    {
   //das löscht den Hintergrund
   super.paintComponent(g);
   Graphics2D g2D = (Graphics2D)g;
  //und dann werden alle 'Commands' abgearbeitet
   for (int i=0; i<cl.size(); i++) {
       cl.get(i).execute(g2D);
   }
    }

Hier sind ein paar Grundlagen erklärt:
http://www.java-forum.org/de/topic46550_zeichnen-swing-tutorial.html
 
G

Guest

Gast
okay cool, danke erstmal :applaus:

Aber ich komme noch immer durcheinander mit den Klassen..

Muss jetzt meine Zelle-Klasse von JComponent erben? Oder von Shape? Und mein Wurm?
Was soll der für ne Klasse sein?

Gott - ich find das so die Hölle :( Inzwischen komme ich mir richtig dumm vor echt..
Diese Klassenansammlung, die Java bietet, ist für Neulinge der reinste Witz.

Ich komme von C/C++ und bin so ultra-hartnäckige Objekt-Orientierung nicht gewohnt.

in C macht man:

funktionDieDasMachtWasIchWill( parameter)

und ich Java:

new JComponent (super(new Color().setSonstWas.UndnochWas()).new WasBidde<X>) implements TooMuch extends JetztReichtsMir; :cry:

Ich hab trotz deines tollen Tutorials keine Ahnung, WAS ich mit einer repaint()-methode, oder drawComponent(), malen soll. Eine Zelle? Einen ganzen Wurm? AAAArgh, ich flipp hier so aus!
Ich kann seit 5 Drecks Tagen keinen Mini superhässlichen Wurm zeichnen!

Shit... BITTE gib mir doch eine kleine Skizze von meinem Wurm! Was das für ne Klasse ist, und aus welchem Typ sein Array besteht (also hat er Rectangles, oder irgendein JComponent, oder waS?)

und mit welchen Objekt (also von welcher Klasse) ich dann losgeh auf die ganzen paint-methoden.. Wäre echt super, mittlerweile blockiert mein Gehirn komplett :bahnhof:
 

Wildcard

Top Contributor
Mach es dir doch einfach:
Eine Klasse Wurm.
Wurm hat unter anderem eine Methode draw(Graphics g).
Da zeichnest du deinen Wurm (ohne irgendwas zu löschen, ohne super.irgendwas, einfach g.drawRect, g.drawLine, whatever.).
Dann noch eine Klasse die von JPanel oder JComponent erbt und die einen Wurm kennt.
Deren paintComponent sieht dann so aus:

Code:
paintComponent(Graphics g)
{
     super.paintComponent(g);
     wurm.draw(g);
}
Danach musst du nur noch panel.repaint() aufrufen wenn du den Wurm bewegst.
 

Ariol

Top Contributor
Hier mal eine Art Snake.
Ich hab das jetzt in einer halben Stunde zusammengeschustert und es ist auch noch keine Kollisionsabfrage u.ä. drin, aber dadurch verstehst du den Code vermutlich besser. Falls nicht, erklär ichs dir morgen.

Falls wieder ein paar Schönheitsfehler drin sind, bitte nicht hauen - es ist schon spät ;)

Code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;

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

public class Snake extends JFrame
{
	public Snake()
	{
		setLayout(new FlowLayout());

		final SnakePanel snakePanel = new SnakePanel();
		snakePanel.setPreferredSize(new Dimension(300, 300));

		add(snakePanel);

		setFocusable(true);
		pack();
		setVisible(true);
		setDefaultCloseOperation(EXIT_ON_CLOSE);

		SwingUtilities.invokeLater(new Runnable()
		{
			public void run()
			{
				snakePanel.requestFocusInWindow();
			}
		});

	}

	public static void main(String[] args)
	{
		new Snake();
	}

	class SnakePanel extends JPanel implements KeyListener
	{
		private final static int	NORTH		= 0;

		private final static int	EAST		= 1;

		private final static int	SOUTH		= 2;

		private final static int	WEST		= 3;

		private int					elementSize	= 10;

		private int					speed		= 100;

		private int					direction	= NORTH;

		ArrayList<Rectangle>		snake		= new ArrayList<Rectangle>();

		Thread						moveThread	= new Thread()
												{
													@Override
													public void run()
													{
														while (true)
														{
															moveSnake(direction);
															try
															{
																sleep(speed);
															}
															catch (InterruptedException e)
															{
															}
														}
													}
												};

		public SnakePanel()
		{
			Rectangle head = new Rectangle(145, 145, elementSize, elementSize);
			snake.add(head);

			for (int i = 0; i < 8; i++)
			{
				addBodyElement();
			}

			addKeyListener(this);
			repaint();
			moveThread.start();
		}

		private void moveSnake(int direction)
		{
			synchronized (snake)
			{
				updateBody();

				Rectangle head = new Rectangle(snake.get(0));

				Point location = head.getLocation();

				int x = location.x;
				int y = location.y;

				switch (direction)
				{
					default:
					case NORTH:
						y -= elementSize;
						break;
					case EAST:
						x += elementSize;
						break;
					case SOUTH:
						y += elementSize;
						break;
					case WEST:
						x -= elementSize;
						break;
				}

				head.setLocation(x, y);

				snake.set(0, head);

				repaint();
			}
		}

		private void updateBody()
		{
			synchronized (snake)
			{
				for (int i = snake.size() - 1; i > 0 ; i--)
				{
					Rectangle r = new Rectangle(snake.get(i-1));

					snake.set(i, r);
				}
			}
		}

		private void addBodyElement()
		{

			synchronized (snake)
			{
				Rectangle end = snake.get(snake.size() - 1);

				Point location = end.getLocation();

				int x = location.x;
				int y = location.y;

				switch (direction)
				{
					default:
					case NORTH:
						y += elementSize;
						break;
					case EAST:
						x -= elementSize;
						break;
					case SOUTH:
						y -= elementSize;
						break;
					case WEST:
						x += elementSize;
						break;
				}

				Rectangle newEnd = new Rectangle(x, y, elementSize, elementSize);

				snake.add(newEnd);

			}
		}

		@Override
		protected void paintComponent(Graphics g)
		{
			super.paintComponent(g);
			g.setColor(Color.BLUE);
			for (Rectangle r : snake)
			{
				g.fillOval(r.x, r.y, r.width, r.height);
			}
		}

		public void keyPressed(KeyEvent e)
		{
			int key = e.getKeyCode();
			if (key == KeyEvent.VK_UP)
				direction = NORTH;
			else if (key == KeyEvent.VK_RIGHT)
				direction = EAST;
			else if (key == KeyEvent.VK_DOWN)
				direction = SOUTH;
			else if (key == KeyEvent.VK_LEFT)
				direction = WEST;
		}

		public void keyReleased(KeyEvent e)
		{

		}

		public void keyTyped(KeyEvent e)
		{

		}
	}

}
 
G

Guest

Gast
okay...also ich hab das jetzt hingekriegt, war aber mehr glück als verstand.

wäre froh darüber, wenn ihr euch das anschaut, ich glaube nämlich ich habe es komlpizierter gemacht als es sein müsste.. hier mein code: (die sachen, die nix mit dem zeichnen zu tun haben, könnt ihr ignorieren. so ist meine momentane move-funktion für den wurm natürlich noch absoluter nonsene, sie bewegt den wurm immer nach unten)

Code:
import java.awt.Rectangle;
import java.awt.Point;
import java.awt.Graphics;

public class Cell extends Rectangle{
	
	private int heading;
	
	/**
	 * Draws the cell.
	 */
	public void draw(Graphics g){
		g.drawRect(x,y,5,5);
	}
	
	/**
	 * CONSTRUCTOR
	 */
	public Cell(int x, int y){
		super(x,y,5,5);
	}
}

Code:
import java.awt.Graphics;

public class Worm{
	
	private Cell[] body;
	private int length;
	
	/**
	 * Draws the worm.
	 */
	public void draw(Graphics g){
		for(int i = 0; i<length; i++){
			body[i].draw(g);
		}
	}
	
	/**
	 * Moves the worm.
	 */
	public void move(){
		body[0] = new Cell((int)body[0].getX(),(int)body[0].getY()+5); 
		body[1] = new Cell((int)body[1].getX(),(int)body[1].getY()+5);
		body[2] = new Cell((int)body[2].getX(),(int)body[2].getY()+5);
	}
	
	/** 
	 * CONSTRUCTOR
	 */
	public Worm(){
		body = new Cell[10];
		body[0] = new Cell(10,20); 
		body[1] = new Cell(10,25);
		body[2] = new Cell(10,30);
		length = 3;
	}
}

Code:
import javax.swing.JComponent;
import java.awt.Graphics;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.FlowLayout; 

public class View extends JFrame{
	
	private GraphicsPanel panel;
	
	/**
	 * CONSTRUCTOR
	 */
	public View(Worm w){
		super();
		setLayout(new FlowLayout());
      	setDefaultCloseOperation(EXIT_ON_CLOSE); 

        panel = new GraphicsPanel(w);
        panel.setPreferredSize(new Dimension(300, 300));
      	add(panel);

      	setFocusable(true);
      	pack();
      	setVisible(true);
	}
}

class GraphicsPanel extends JPanel{
	
	private Worm w;
	
	/**
	 * @Override
	 */ 
	protected void paintComponent(Graphics g){
		super.paintComponent(g);
     	w.draw(g);
	}
	
	/**
	 * CONSTRUCTOR
	 */
	GraphicsPanel(Worm w){
		this.w = w;
	}
}
Code:
public class Snake{
	
	public static void main(String[] args){
	
		Worm w = new Worm();
		View v = new View(w);
		
		int i = 50;
		while (--i > 0){
			w.move();
			try{
				Thread.sleep(20);
			}
			catch(InterruptedException e){;}
		}		
	}
}

und dann hab ich noch eine Frage: meine while-schleife in der main-methode bewegt den wurm nicht.
wenn ich das ganze mit dem thread und sleep usw. weglasse, dann geht es schon!
also die funktion move() funktioniert, nur irgendwie geht sie nicht mehr, wenn ich das mit den threads mache?
wieso?
wie muss ich das machen, dass ich immer ne kleine pause habe?
 

Ariol

Top Contributor
Weil du kein repaint von v machst, nachdem du w.move() aufgerufen hast.

Für deine Pausen ein kleines Beispiel:

Code:
new Thread()
{
    public void run()
    {
          while(true)
          {
               System.out.println("Hallo");
               try
               {
                      sleep(1000);
                }
                catch(InterruptedException e){;} 
           }
     }
}.start;

Damit wird einmal pro Sekunde Hallo auf die Konsole geschrieben.
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben