Adk bzw Curve Fever Klon

Hey Leute, ich weiß nicht ob ihrs kennt, aber ich will einen kleinen adk bzw Curve Feverklon zaubern.
Das ganze ist ja eigentlich nur ein multiplayer snake mit runder bewegung.
Nun hab ich eine kleine simulation gebastelt, wobei ich aber wieder mal probleme habe:
Ich benutze für den spieler einfach nen path2D, der dann mit einem Basic Stroke gezeichent wird.
Auf tastendruck A (links) und D (rechts) verändert sich der bewegungswinkel. Noch ohne kollisionserkennung und so weiter.

  • das ganze läuft von anfang an nicht wirklcih flüssig (es soll später über lan laufen, ist das überhaupt realistisch?)
  • nach ungefähr 1 minute, wenn sich eshr viele punkte im Path2D angesammelt haben, läggt es nur noch
    (die schlange macht 5 pixel schritte, ungefähr…)
  • Es gibt, wenn man sich selbst oft schneidet oft schwarze punkte IM stroke
  • das ganze sieht trotz antialiasing nicht wirklich antialiased aus…

Könnt ihr mir weiter helfen?
vielen Dank!

Der Code bisher:

Main.java
[spoiler]```package de.skysoldier.adk;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

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

public class Main extends JPanel implements Runnable {

private Player player;
private boolean rotateLeft;
private boolean rotateRight;
private long lastFrame;
private long delta;

public Main(){
	JFrame f = new JFrame();
	f.setExtendedState(JFrame.MAXIMIZED_BOTH);
	f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	f.setLocationRelativeTo(null);
	addKeyListener(movementListener);
	setFocusable(true);
	f.add(this);
	f.setVisible(true);
	Thread t = new Thread(this);
	t.start();
}

public void run(){
	player = new Player(Color.GREEN.darker());
	lastFrame = System.nanoTime();
	while(true){
		updateTime();
		updatePlayer();
		repaint();
		try{Thread.sleep(5);}catch(Exception e){}
	}
}

private void updateTime(){
	long thisf = System.nanoTime() - lastFrame;
	delta = thisf - lastFrame;
}

private void updatePlayer(){
	player.update(delta, rotateLeft, rotateRight);
}

private KeyAdapter movementListener = new KeyAdapter() {
	public void keyPressed(KeyEvent e){
		if(e.getKeyCode() == KeyEvent.VK_A) rotateLeft = true;
		if(e.getKeyCode() == KeyEvent.VK_D) rotateRight = true;
	}
	public void keyReleased(KeyEvent e){
		if(e.getKeyCode() == KeyEvent.VK_A) rotateLeft = false;
		if(e.getKeyCode() == KeyEvent.VK_D) rotateRight = false;
	}
};

public void paintComponent(Graphics g){
	Graphics2D g2d = (Graphics2D) g;
	g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
	g2d.setColor(Color.BLACK);
	g2d.fillRect(0, 0, getWidth(), getHeight());
	g2d.setStroke(player.getStroke());
	g2d.setColor(player.getColor());
	g2d.draw(player.getPath());
	g2d.setColor(player.getColor().darker());
	g2d.fillOval((int) player.getPositionX() - player.getWidth() / 2, (int) player.getPositionY() - player.getWidth() / 2, player.getWidth(), player.getWidth());
	g2d.setColor(player.getColor().brighter());
	g2d.fillOval((int) player.getPositionX() - player.getWidth() / 4, (int) player.getPositionY() - player.getWidth() / 4, player.getWidth() / 2, player.getWidth() / 2);
}

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

}```[/spoiler]

Player.java
[spoiler]```package de.skysoldier.adk;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Stroke;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;

public class Player {

private Path2D playerPath;
private BasicStroke playerStroke;
private Color color;
private int width;
private double positionX;
private double positionY;
private double speedX;
private double speedY;
private double movementX;
private double movementY;
private double angle;
private double rotationSpeed;

public Player(Color color){
	this.color = color;
	this.width = 20;
	this.positionX = 400;
	this.positionY = 300;
	this.speedX = 2.5;
	this.speedY = 2.5;
	this.rotationSpeed = 0.05;
	this.angle = Math.toRadians(0);
	playerPath = new GeneralPath();
	playerStroke = new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
	playerPath.moveTo(positionX, positionY);
}

public void update(long delta, boolean rotateRight, boolean rotateLeft){
	if(rotateRight) angle += rotationSpeed * delta / 1e15;
	else if(rotateLeft) angle -= rotationSpeed * delta / 1e15;
	movementX = Math.cos(angle) * speedX * delta / 1e15;
	movementY = Math.sin(angle) * speedY * delta / 1e15;
	positionX += movementX;
	positionY += movementY;
	playerPath.lineTo(positionX, positionY);
}

public void rotate(double angle){
	this.angle = Math.toRadians(angle);
}

public int getWidth(){
	return width;
}

public double getPositionX(){
	return positionX;
}

public double getPositionY(){
	return positionY;
}

public double getAngle(){
	return angle;
}

public Color getColor(){
	return color;
}

public Path2D getPath(){
	return playerPath;
}

public Stroke getStroke(){
	return playerStroke;
}

}```[/spoiler]

*** Edit ***

Ach ja, ausserdem tritt nach einer weile ab und zu diese exception auf, obwohl mein code persönlich nichts mit arrays am hut hat…
was hat path2d da für probleme??

xception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException
	at java.awt.geom.AffineTransform.transform(Unknown Source)
	at java.awt.geom.Path2D$Float$TxIterator.currentSegment(Unknown Source)
	at sun.dc.DuctusRenderingEngine.getAATileGenerator(Unknown Source)
	at sun.java2d.pipe.AAShapePipe.renderPath(Unknown Source)
	at sun.java2d.pipe.AAShapePipe.draw(Unknown Source)
	at sun.java2d.pipe.PixelToParallelogramConverter.draw(Unknown Source)
	at sun.java2d.SunGraphics2D.draw(Unknown Source)
	at de.skysoldier.adk.Main.paintComponent(Main.java:72)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JComponent.paintToOffscreen(Unknown Source)
	at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
	at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
	at javax.swing.RepaintManager.paint(Unknown Source)
	at javax.swing.JComponent._paintImmediately(Unknown Source)
	at javax.swing.JComponent.paintImmediately(Unknown Source)
	at javax.swing.RepaintManager$3.run(Unknown Source)
	at javax.swing.RepaintManager$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.access$1100(Unknown Source)
	at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.awt.EventQueue.access$200(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)

Die Exception: Das update(…) wird wohl von einem anderen Thread aufgerufen, als dem EDT. Der EDT liest den Path (bzw. versucht, ihn zu zeichnen), und während er das macht, wird ihm der Path von einem anderen Thread unterm Hintern weg-geändert. (Dieses deja vu hatte ich doch schonmal?!)

Zur Performance… wenn das ganze mit einem delay von 10 ms läuft, sammelt er in einer Minute 6000 Punkte an. Das als geantialiasten Pfad zeichnen könnte je nach Rechner schon belastend sein. (Und dass das bei so kleinen Schritten auch nicht mehr geantialiast aussieht, ist auch klar…)

und wie wäre das nun besser o.O
soll ich eventuell nicht den ganzen path neu zeichnen?

Das wäre eine Möglichkeit. Du könntest auf ein BufferedImage zeichnen (nur die Pfadsegmente die neu entstanden sind) und das BufferedImage dann in der paintComponent zeichnen.

Das könnte schwierig werden. Wenn man einen Path, der aus zwei Segmenten besteht, auf EINmal zeichnet, wird der Übergang dazwischen entsprechend der JOIN_*-Flags “schön gemacht”. Wenn man die beiden Segmente einzeln zeichnet, könnte man meinen, dass es genauso aussehen müßte, aber … der Übergang kann dann eben nicht schön gemacht werden. Je nachdem, welchen Stroke+Paint man verwendet, sieht das dann “etwas unschön” bis “total kacke” aus.
Die Mechanismen, die für dieses “schön machen” verantwortlich sind, sind erstaunlich kompliziert (da hatte ich kürzlich mal oberflächlich reingeschaut…). Eine Patentlösung wüßte ich da spontan nicht. Aber bei einem einfachen BasicStroke und einem einfachen Paint (d.h. Color), und mit CAP_ROUND, dürften die Übergänge auch beim Zeichnen der einzelnen Segmente zumindest “OK” aussehen - man könnte es einfach mal versuchen…

hmm das heisst ich müsste auch JOIN_ROUND sozusagen verzichten… aber funktioniert das auch im multiplayer?

*** Edit ***

Stimmt, ich müsste ja einfach nur in ner schleife auf dem punkt enemy.getPath().currentPoint() nen neuen kreis zeichnen… oder?

Das JOIN_*-Flag kommt ja überhaupt nicht zum Tragen, wenn man nur einzelne Segmente zeichnet … ?!

ja, das ist mir klar…
aber was soll ich nun tun o.O
ein praktischer vorschlag? :confused:

Also dem Originalspiel nach wäre der angedeutete BufferedImage-Ansatz da wohl besser. Wie soll man auch sonst z.B. die Unterbrechungen in den Prad einbauen?

ok das heitßt also nix mit path2D oder wie? wie will ich einen path2D denn bitte nur teilweise zeichnen?
dann eben doch mit einzelnen kreisen? o.O

seufz … du hast dieses Spiel gesehen, und dir gedacht: “Hey, das programmier’ ich auch. In Java. Ich kann ja Java programmieren”. Es ist eben nicht so einfach. Ich weiß nicht, was “die beste” Lösung ist, um das zu erreichen, was du erreichen willst. Aber wenn ich das Spiel dort nachprogrammieren sollte, würde ich da wohl eher ein BufferedImage verwenden, und keine Path2Ds. Aber das muss dich nicht beeinflussen. Vielleicht postet hier noch jemand gute Ideen zum Path2D-Ansatz.

nenenenenenee da verstehst du was falsch.
ich will nur einen klon erstellen der das repräsentiert, sprich eine linie die halt auf dem bildschirm bleibt und nicht laggt.
wie du richtig bemerkst fehlt mir der ansatz zur “performantesten lösung” mit der kollisionserkennung möglich ist.
es sind ja nur 2 teile:

-die schlange malen mit unterbrechnugen (Irendwie)
-kollisionserkennung

ich hab das jetzt gemacht mit dem image. das heißt ich hab jetzt quadra buffering, double jpanel double ich ^^
Die Sache ist nur wie ich jetzt die kollisionserkennung im multiplayer mache. aber das schau ich später nochmal.
Jetzt hab ich aber erstmal den stroke und den path2D komplett weggelassen, ich zeichne halt jeden frame nur einen kreis
aus x y.

Das Spiel kenne ich übrigens schon sehr lange, hab halt nur eine idee für eine mehr oder weniger kleine herausforderung gesucht.

Und bekommst Du damit jetzt glatte Linien hin?

im sinne von anti aliasing? ja besser jedenfalls.