Überlappende Steine

Status
Nicht offen für weitere Antworten.

mvitz

Top Contributor
Hallo zusammen,

da ich immer mal wieder mit dem Gedanken gespielt habe doch endlich mal ein kleines Java Projekt zu starten und hier so häufig Tetris auftaucht, hab ich damit auch mal begonnen.

Als Basis habe ich das Tutorial von Quaxli für 2D Spieleprogrammierung genommen. Soweit funktioniert auch schon einiges nur folgendes passt bisher nicht (siehe Anhang).

Wenn Steine aufeinander fallen, kann es dazu kommen, dass diese überlappen. Ich vermute das liegt daran, da meine Steine von der Klasse java.awt.geom.Rectangle2D.Double erbt und diese nunmal das Koordinatensystem mit double verwaltet. Gibt es eine Möglichkeit ein Integer Koordinatensystem zu bekommen?

Tetris.jpg
 

hdi

Top Contributor
Wofür nutzt du denn double? Steig doch gleich auf int um, also nimm normale Rectangles.
Halbe pixel gibt es eh nicht.
 

mvitz

Top Contributor
Also habe jetzt direkt von java.awt.Rectangle geerbt. Der Fehler ist jedoch immer noch vorhanden. Ich denke fast, er hängt mit meiner Kollisionsabfrage (lediglich per x.intersects(s)) zusammen. Werde mir das gleich nachm essen nochmal anschauen.
 

mvitz

Top Contributor
[HIGHLIGHT="Java"]
public class GamePanel extends JPanel implements Runnable, KeyListener {

private static final long serialVersionUID = 1L;
private boolean gameRunning = true;
private List<Sprite> oldBlocks;
private Sprite actualBlock;
private final int startHeight = 2 * Sprite.SQUARE;
private final int startWidth = 6 * Sprite.SQUARE;

public GamePanel(int theWidth, int theHeight) {
Dimension dimension = new Dimension(theWidth, theHeight);
setPreferredSize(dimension);
setMaximumSize(dimension);
setMinimumSize(dimension);
JFrame frame = new JFrame("Tetris");
frame.setLocation(100, 100);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.addKeyListener(this);
frame.add(this);
frame.pack();
frame.setVisible(true);
doInit();
}

private void doInit() {
last = System.nanoTime();
oldBlocks = new LinkedList<Sprite>();
actualBlock = getRandomBlock();
Thread thread = new Thread(this);
thread.start();
}

@Override
public void run() {
while (gameRunning) {
computeDelta();
doLogic();
moveObjects();
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO: handle exception
}
}
}

private void moveObjects() {
actualBlock.move(delta);
if (!actualBlock.canMove()) {
oldBlocks.add(actualBlock);
System.out.println(actualBlock.getY());
if (actualBlock.getY() < startHeight + Sprite.SQUARE) {
gameRunning = false;
} else {
actualBlock = getRandomBlock();
}

}
}

private void doLogic() {
actualBlock.doLogic(delta);
for (Sprite sprite : oldBlocks) {
if (actualBlock.intersects(sprite)) {
actualBlock.setMoved(false);
}
}
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.drawString("FPS: " + Long.toString(fps), 20, 10);
actualBlock.drawObjects(g);
for (Drawable drawable : oldBlocks) {
drawable.drawObjects(g);
}
}
}[/HIGHLIGHT]
 

hdi

Top Contributor
Also nur geraten weil man sieht ja nicht den ganzen Code:

Code:
actualBlock.move(delta);
if (!actualBlock.canMove())

Erstens mal denk ich, solltest du erst abfragen, und dann bewegen.
Sonst isses ja schon zu spät?

Aber vllt rechnest du auch vor. Aber mmN braucht die Methode canMove()
doch noch den Parameter delta? Wie willst du sonst bestimmen, ob er sich bewegen
darf, wenn du nich mal weisst um wieviel er sich bewegen soll.

Aber vllt versteh ich auch den Code nicht.
Ich kapier auch nicht was startHeight und startWidth sein soll.
Hast du das vllt verdreht? Dein Spielfeld liegt doch nicht horizontal oder.

Ich denke es wär noch hilfreich wenn du mal die canMove() Methode zeigst,
sowie die move() Methode.
 

mvitz

Top Contributor
Also:

Das geht bei mir so, da:
[HIGHLIGHT="Java"] @Override
public void doLogic(long delta) {
if (parent.getHeight() - height<= y) {
speed = 0;
moves = false;
}
}

@Override
public void move(long delta) {
y += speed;
}[/HIGHLIGHT]
und doLogic vor move aufgerufen wird. (Habs jetzt aber trotzdem geändert:
[HIGHLIGHT="Java"] private void moveObjects() {
if (actualBlock.canMove()) {
actualBlock.move(delta);
} else {
oldBlocks.add(actualBlock);
if (actualBlock.getY() < startHeight + Sprite.SQUARE) {
gameRunning = false;
} else {
actualBlock = getRandomBlock();
}
}
}[/HIGHLIGHT]
Die innere if-Abfrage dient dazu, ob die Steine oben überquellen und das Spiel somit zu Ende ist.

Die delta übergaben habe ich bisher noch garnicht benutzt (sind quasi überbleibsel aus dem Tutorial)

startHeight und startWidth ist der Punkt wo ein neuer Block oben erscheint. startHeight ist praktisch nur ein Abstand (zu der FPS anzeige) und startWidth lässt den Block halt mittig und nicht am linken Rand starten.
Ich denke mal es wird an der Kollisionsabfrage liegen.
 

Quaxli

Top Contributor
Ich hab' auch mal ein Tetris angefangen, aber nie zu Ende gemacht. Die Basics zum GameLoop hatte ich so wie im Tutorial. Aber bei Tetris gibt es ja Probleme, die Steine zu drehen, zu plazieren und teilweise zu löschen.
Ich hatte das damals wie folgt gelöst:
Ich hatte mir eine Klasse namens Grid geschrieben, die das ganze Spielfeld in ein Gitter bzw. ein Rechteck-Array unterteilt hat.
Meine Steine waren ebenfalls Arrays: 5x5-Rectangle (dabei natürlich etliche die null waren). Das Ganze sah dann etwa so aus:

x x x x x
x o o x x
x x o x x
x x o x x
x x o x x

Damit hat sich auch die Drehung unkompliziert lösen lassen und es war einfach zu berechnen, wo die Einzelteile nach der Drehung angkommen.

x x x x x
x x x o x
o o o o x
x x x x x
x x x x x

Wenn der Stein unten angekommen ist, habe ich die Rechtecke aus dem Stein-Array in das Spiel-Gitter übernommen.
Über dieses Gitter habe ich dann auch die Kollisionsprüfung vorgenommen und ermittelt, wo der nächste Stein plaziert werden muß. Außerdem ist es dann einfacher zu prüfen, ob eine Reihe komplett gefüllt ist und es gibt ebenfalls weniger Probleme wenn man eine Reihe mittendrin löschen muß.
 

Marco13

Top Contributor
Das wäre eher "Pentris" :D Aber die eigentlich spannende Frage ist ja: Wenn man die Untere Situation hat, und der Block (also nicht das Grid, sondern der eingezeichnete Block) EINE Reihe über dem Boden ist (wenn das Grid also schon eine Zeile aus dem Spielfeld hinausragt) und man dreht dann (so dass die obere Situation entsteht) - wie geht das dann? Geht das überhaupt? Was passiert dann?
 

mvitz

Top Contributor
Evtl. sollte ich dann doch erstmal etwas einfacheres machen ;)

Aber das mit dem verschieben ist doch irgendwie komisch. Evtl. hat da ja doch noch jemand einen Idee. Folgende Demo zeigt das Problem in größer:
[HIGHLIGHT="Java"]import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.LinkedList;
import java.util.List;

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

public class Test extends JPanel implements Runnable {

private static final long serialVersionUID = 1L;

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

public static final int SQUARE = 256;
private List<Square> oldSquares;
private Color[] colors;
private Square square;
private int colorIterator;
private boolean gameRunning;

public Test() {
setPreferredSize(new Dimension(SQUARE, 5 * SQUARE / 2));
JFrame frame = new JFrame("Tests");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(this);
frame.pack();
frame.setVisible(true);
doInit();
}

private void doInit() {
gameRunning = true;
colorIterator = 0;
colors = new Color[] { Color.CYAN, Color.YELLOW };
oldSquares = new LinkedList<Square>();
square = new Square(colors[colorIterator++], this);
Thread thread = new Thread(this);
thread.start();
}

@Override
public void run() {
while (gameRunning) {
moveObject();
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO: handle exception
}
}
}

private void moveObject() {
boolean aCollision = false;
for (Square aSquare : oldSquares) {
if (square.intersects(aSquare)) {
aCollision = true;
break;
}
}
if (square.canMove() && !aCollision) {
square.move();
} else {
oldSquares.add(square);
if (square.y <= 0) {
gameRunning = false;
} else {
square = new Square(colors[colorIterator++], this);
if (colorIterator >= colors.length) {
colorIterator = 0;
}
}
}

}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = -1, j = 1; i < getHeight(); i += (SQUARE / 2), j++) {
if (j % 2 == 0) {
g.setColor(Color.BLACK);
} else {
g.setColor(Color.WHITE);
}
g.fillRect(SQUARE / 2, i, SQUARE, SQUARE / 2);
}
for (Square aSquare : oldSquares) {
aSquare.draw(g);
}
square.draw(g);
}

class Square extends Rectangle {
private static final long serialVersionUID = 1L;
private Color color;
private int gravity;
private boolean canMove;
private int size;
private Test parent;

public Square(Color theColor, Test theParent) {
super();
color = theColor;
gravity = 1;
canMove = true;
size = SQUARE / 2;
parent = theParent;
x = 0;
y = 0;
width = size;
height = size;
}

public void draw(Graphics theGraphics) {
theGraphics.setColor(color);
theGraphics.fillRect(x, y, size, size);
theGraphics.setColor(Color.BLACK);
theGraphics.drawRect(x, y, size, size);
}

public void move() {
if (y + size + 2 * gravity >= parent.getHeight()) {
stop();
}
y += gravity;
}

public void stop() {
canMove = false;
}

public boolean canMove() {
return canMove;
}
}
}[/HIGHLIGHT]
 

Quaxli

Top Contributor
Das wäre eher "Pentris" :D Aber die eigentlich spannende Frage ist ja: Wenn man die Untere Situation hat, und der Block (also nicht das Grid, sondern der eingezeichnete Block) EINE Reihe über dem Boden ist (wenn das Grid also schon eine Zeile aus dem Spielfeld hinausragt) und man dreht dann (so dass die obere Situation entsteht) - wie geht das dann? Geht das überhaupt? Was passiert dann?
Dann formatiert es Deine Festplatten, tötet Deinen Hund und sprengt den Planeten ;)

Mal im Ernst: Daß hatte ich seinerzeit abgefangen und dann eben nicht gedreht. Sobald bei der Zielkonstellation ein Teilstein außerhalb des Spielfeldes ist, wurde nicht mehr gedreht. Ich hatte das damals etwas umständlich gelöst. Mit der Lösung, die Du vor kurzem im Anfängerforum zu einem Schachbrett vorgeschlagen hast - nämlich das Gitter etwas größer zu definieren - würde es vermutlich etwa eleganter gehen.
 
Zuletzt bearbeitet:

Quaxli

Top Contributor
@habi55:

Nachdem eine Kollision ja erst zustande kommen kann, wenn die Steine sich überlappen, mußt Deinen Stein dann halt noch zurücksetzen:

[highlight=Java] private void moveObject() {
boolean aCollision = false;
for (Square aSquare : oldSquares) {
if (square.intersects(aSquare)) {
square.y = aSquare.y - square.height; //<<< Position anpaasen
aCollision = true;
break;
}
}[/highlight]

Für Dein Demo war diese Modifikation ausreichend (in meinen Augen)
 

mvitz

Top Contributor
Danke, das wars :D

Habe mich zwar jetzt TicTacToe zugewandt (ist für den Anfang dann doch einfacher). Und habe das BasisSpiel auch schon fertig (auch wenn mein Gewinnüberprüfungsalgo wohl atm ziemlich beschiessen ist).

(Gibts hier keinen "Thema gelöst" button?)
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen

Ähnliche Java Themen


Oben