Ich arbeite an einem kachelbasierten Mario-Klon.
Dies funktioniert gut beim Gehen und Fallen. Aber wenn der Spieler in die Nähe einer Wand springt und nach rechts in die Luft geht, bleibt der Spieler an der Wand hängen. Das Spieler-Sprite fällt erneut, wenn der Spieler den Schlüssel loslässt.
Das Setup ist ziemlich einfach und ich kann das Problem nicht finden. Die Karte wird als 2D-Array mit Kartenblöcken aufgebaut. Ein Block kann fest sein oder nicht. Der Spieler kann sich nicht durch feste Objekte bewegen, duh ..
In der Spielschleife:
- Der Standort des Spielers wird aktualisiert (Schwerkraft, Bewegung usw.).
- Überprüfen Sie die Karte auf Kollisionen. Wenn eine Kollision im Y gefunden wird, wird der Standort des Spielers so aktualisiert, dass er über oder unter dem Block liegt (abhängig von der Richtung des Spielers), und das Kollisionsfeld wird mit dem neuen Standort aktualisiert. Dann der gleiche Vorgang für das X.
- Das Kollisionsfeld wird auf den neuen Speicherort (einen freien Speicherort) aktualisiert. Das Feld ist so eingestellt, dass es etwas höher ist, wenn der Block unter dem Spieler überprüft, ob er gelandet ist. Dies dient dazu, den Status des Spielers vom fliegenden Sprite zum Leerlauf zu ändern.
Ich habe auch versucht, die X- und Y-Prüfung so umzuschalten, dass der Player auf der X-Linie bewegt wird. Wenn sich der Spieler dann bewegt, wird die Bewegung sehr verzögert. Wenn ich den Knopf drücke und loslasse, um mich zu bewegen, bewegt sich der Spieler schneller, aber in Spannfuttern. Sehr trippy ..
Hat jemand den Fehler gesehen oder kann mir dafür einen besseren Kollisionsalgorithmus geben?
UPDATE (Code wurde nicht aktualisiert)
Ich habe die x- und y-Prüfmethode ausgetauscht und die isonland-Variable implementiert. Wenn Sie also gegen Wände gehen und springen, funktioniert dies perfekt. Erst jetzt, wenn der Spieler springt, wird Mario bei der Landung zurückgesetzt. Dies liegt daran, dass die X-Check-Methode zuerst die Position von Mario anpasst.
Wie kann ich das lösen?
Aktualisierungsmethode für Kartenklassen:
public void update(int timeElapsed) {
//update entities
for(Entity entity : _mapEntities) {
entity.update(timeElapsed);
}
//update objects
for(MapObject mapObt : _mapObjects) {
mapObt.update(timeElapsed);
}
//check for collisions
checkMapCollision();
}
Aktualisierungsmethode für Entitäten (abstrakt):
public void update(int timeElapsed) {
_velocity = new Vector2d(0.0F, 0.0F);
//add gravity
_velocity.y = Map._GRAVITY_PER_SEC * timeElapsed;
}
Mario (erweitert Entität) Update Methos:
@Override
public void update(int timeElapsed) {
super.update(timeElapsed);
if(_state == STATES.IDLE) {
} else if(_isMoving) {
_marioSmallWalk.update(timeElapsed);
}
if(_state == STATES.JUMPING) {
setVelocityY(getVelocity().y + _jumpSpeed);
_jumpSpeed += _JUMP_DECREASE * timeElapsed;
//falling?
if(getVelocity().y > 0) {
setState(STATES.FALLING);
}
}
if(_isMoving) {
double walkSpd = (_WALK_SPEED_SEC * timeElapsed);
if(getFacing() == FACING.LEFT) {
walkSpd = -walkSpd;
}
setVelocityX(getVelocity().x + walkSpd);
}
//falling?
if(getVelocity().y > (Map._GRAVITY_PER_SEC * timeElapsed) + 1.0F) {
setState(STATES.FALLING);
}
setPosition((int)(getX() + getVelocity().x), (int)(getY() + getVelocity().y));
}
Kartenklasse CheckMapCollision-Methode:
public void checkMapCollision() {
//enteties move so check it
for(Entity entity : _mapEntities) {
//get the corners
Rectangle bounds = entity.getBounds();
Block[] corners = getCornerBlocks(bounds);
Vector2d dir = entity.getDirection();
//moving down
if(dir.y > 0) {
if(corners[2].isSolid() || corners[3].isSolid()) {
Rectangle blkBounds = null;
if(corners[2].isSolid()) {
blkBounds = corners[2].getBounds();
} else {
blkBounds = corners[3].getBounds();
}
entity.setPositionY(blkBounds.y);
}
} else {
if(corners[0].isSolid() || corners[1].isSolid()) {
Rectangle blkBounds = null;
if(corners[0].isSolid()) {
blkBounds = corners[0].getBounds();
} else {
blkBounds = corners[1].getBounds();
}
entity.setPositionY(blkBounds.y + blkBounds.height + bounds.height);
}
}
bounds = entity.getBounds();
corners = getCornerBlocks(bounds);
//moving to the right
if(dir.x > 0) {
if(corners[1].isSolid() || corners[3].isSolid()) {
Rectangle blkBounds;
if(corners[1].isSolid()) {
blkBounds = corners[1].getBounds();
} else {
blkBounds = corners[3].getBounds();
}
entity.setPositionX(blkBounds.x - (bounds.width-entity.getCurrentSprite().getOffsetX())-1);
}
} else {
if(corners[0].isSolid() || corners[2].isSolid()) {
Rectangle blkBounds;
if(corners[0].isSolid()) {
blkBounds = corners[0].getBounds();
} else {
blkBounds = corners[2].getBounds();
}
entity.setPositionX(blkBounds.x + blkBounds.width + (bounds.width/2));
}
}
bounds = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height+1);
corners = getCornerBlocks(bounds);
//moving down
if(dir.y > 0) {
if(corners[2].isSolid() || corners[3].isSolid()) {
Rectangle blkBounds = null;
if(corners[2].isSolid()) {
blkBounds = corners[2].getBounds();
} else {
blkBounds = corners[3].getBounds();
}
entity.landed();
System.out.println("landed");
}
}
}
}
quelle
entity.setPositionX()
oderentity.setPositionY()
wird nach der Kollisionsprüfung aufgerufen. Wenn ich ohne zu springen gegen die Wand gehe, wird der Spieler richtig zurückgeschoben.Antworten:
Ihr Sprite-Charakter bleibt an den Schnittpunkten zwischen zwei Kacheln hängen. Das folgende Diagramm zeigt die Situation zu Beginn Ihrer Kollisionsroutine.
Mario wurde aufgrund der Spielerkontrolle nach rechts verschoben. Dadurch wird das Sprite in den drei Kacheln platziert. Anschließend führen Sie die Kollisionserkennung nach unten Y durch. Beachten Sie den Punkt im blauen Kreis: Dadurch wird eine Kollision mit dem zweiten Block erkannt und versucht, diese zu beheben, indem die Y-Koordinate auf die Obergrenze dieses Blocks gesetzt wird, wodurch Mario effektiv angehalten wird in der Luft.
Sobald der Spieler den Schlüssel loslässt, um Mario nach rechts zu bewegen, bewegt sich das Sprite nicht mehr in die Wand, und der Punkt, an dem zuvor eine Kollision festgestellt wurde, registriert keinen Treffer. Dadurch kann Mario zu Boden fallen.
Die schnelle und schmutzige Lösung für dieses Problem besteht darin, zuerst X-Kollisionen zu lösen und die Schwerkraft nur anzuwenden, wenn sich der Spieler nicht am Boden befindet. Wenn Sie die Schwerkraft beibehalten würden, würde das gleiche Problem beim Herumlaufen auftreten (dies ist das "trippige" Verhalten, das Sie bereits nach dem Vertauschen des X- und Y-Checks gesehen haben;)).
quelle
Ich habe herausgefunden, was ich falsch gemacht habe.
Der Schlüssel liegt darin, wann Sie die Position aktualisieren. Was ich falsch gemacht habe, war, dass ich die Methode "zuerst die Positionen ändern und dann überprüfen und anpassen" durchgeführt habe und sie in der Reihenfolge ausführen, überprüfen und dann aktualisieren musste.
Mein vorheriger Code hat Folgendes getan:
Dies führte dazu, dass nach dem Sprung des Spielers Marios Position in der Aktualisierungsmethode geändert wurde, sodass sich die Kollisionsbox in den Bodenplättchen befand. Dann änderte die Kollisionsmethode die X-Position in das x der linken unteren Ecke, und dann bewegte das y sie über die Bodenkachel. Dies bedeutet, dass Mario jedes Mal, wenn der Spieler von einem Sprung landet, um einige Pixel zurückgeschoben wird.
Ich habe versucht, die X- und Y-Prüfmethoden auszutauschen, aber dann ändert sich das Problem einfach in etwas anderes (die Wandumarmung).
Nun habe ich die Lösung wie folgt geändert:
Jetzt wird das X zuerst überprüft / geändert und dann wird das Kollisionsfeld mit dem neuen Entitätsspeicherort aktualisiert. Dann wird das Y überprüft / geändert.
Das funktioniert wie ein Zauber :)
quelle