Fondamentalement, je veux empêcher la caméra de se déplacer en sous-pixels, car je pense que cela conduit à des sprites qui modifient visiblement leurs dimensions, même si c'est très légèrement. (Y a-t-il un meilleur terme pour cela?) Notez qu'il s'agit d'un jeu pixel-art où je veux avoir des graphismes pixelisés nets. Voici un gif qui montre le problème:
Maintenant, ce que j'ai essayé était le suivant: déplacez la caméra, projetez la position actuelle (il s'agit donc des coordonnées de l'écran), puis arrondissez ou transformez en int. Ensuite, convertissez-le en coordonnées mondiales et utilisez-le comme nouvelle position de la caméra. Autant que je sache, cela devrait verrouiller la caméra aux coordonnées réelles de l'écran, et non à des fractions de celles-ci.
Pour une raison quelconque, cependant, la y
valeur de la nouvelle position explose. En quelques secondes, il augmente à quelque chose comme 334756315000
.
Voici un SSCCE (ou un MCVE) basé sur le code du wiki LibGDX :
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
public class PixelMoveCameraTest implements ApplicationListener {
static final int WORLD_WIDTH = 100;
static final int WORLD_HEIGHT = 100;
private OrthographicCamera cam;
private SpriteBatch batch;
private Sprite mapSprite;
private float rotationSpeed;
private Viewport viewport;
private Sprite playerSprite;
private Vector3 newCamPosition;
@Override
public void create() {
rotationSpeed = 0.5f;
playerSprite = new Sprite(new Texture("/path/to/dungeon_guy.png"));
playerSprite.setSize(1f, 1f);
mapSprite = new Sprite(new Texture("/path/to/sc_map.jpg"));
mapSprite.setPosition(0, 0);
mapSprite.setSize(WORLD_WIDTH, WORLD_HEIGHT);
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
// Constructs a new OrthographicCamera, using the given viewport width and height
// Height is multiplied by aspect ratio.
cam = new OrthographicCamera();
cam.position.set(0, 0, 0);
cam.update();
newCamPosition = cam.position.cpy();
viewport = new ExtendViewport(32, 20, cam);
batch = new SpriteBatch();
}
@Override
public void render() {
handleInput();
cam.update();
batch.setProjectionMatrix(cam.combined);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
mapSprite.draw(batch);
playerSprite.draw(batch);
batch.end();
}
private static float MOVEMENT_SPEED = 0.2f;
private void handleInput() {
if (Gdx.input.isKeyPressed(Input.Keys.A)) {
cam.zoom += 0.02;
}
if (Gdx.input.isKeyPressed(Input.Keys.Q)) {
cam.zoom -= 0.02;
}
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
newCamPosition.add(-MOVEMENT_SPEED, 0, 0);
}
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
newCamPosition.add(MOVEMENT_SPEED, 0, 0);
}
if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
newCamPosition.add(0, -MOVEMENT_SPEED, 0);
}
if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
newCamPosition.add(0, MOVEMENT_SPEED, 0);
}
if (Gdx.input.isKeyPressed(Input.Keys.W)) {
cam.rotate(-rotationSpeed, 0, 0, 1);
}
if (Gdx.input.isKeyPressed(Input.Keys.E)) {
cam.rotate(rotationSpeed, 0, 0, 1);
}
cam.zoom = MathUtils.clamp(cam.zoom, 0.1f, 100 / cam.viewportWidth);
float effectiveViewportWidth = cam.viewportWidth * cam.zoom;
float effectiveViewportHeight = cam.viewportHeight * cam.zoom;
cam.position.lerp(newCamPosition, 0.02f);
cam.position.x = MathUtils.clamp(cam.position.x,
effectiveViewportWidth / 2f, 100 - effectiveViewportWidth / 2f);
cam.position.y = MathUtils.clamp(cam.position.y,
effectiveViewportHeight / 2f, 100 - effectiveViewportHeight / 2f);
// if this is false, the "bug" (y increasing a lot) doesn't appear
if (true) {
Vector3 v = viewport.project(cam.position.cpy());
System.out.println(v);
v = viewport.unproject(new Vector3((int) v.x, (int) v.y, v.z));
cam.position.set(v);
}
playerSprite.setPosition(newCamPosition.x, newCamPosition.y);
}
@Override
public void resize(int width, int height) {
viewport.update(width, height);
}
@Override
public void resume() {
}
@Override
public void dispose() {
mapSprite.getTexture().dispose();
batch.dispose();
}
@Override
public void pause() {
}
public static void main(String[] args) {
new Lwjgl3Application(new PixelMoveCameraTest(), new Lwjgl3ApplicationConfiguration());
}
}
et voici lesc_map.jpg
et ledungeon_guy.png
Je serais également intéressé à en apprendre davantage sur des moyens plus simples et / ou meilleurs de résoudre ce problème.
Réponses:
Votre problème ne consiste pas à déplacer l'appareil photo par incréments de pixels. C'est que votre rapport texel / pixel est légèrement non entier . J'emprunterai quelques exemples de cette question similaire à laquelle j'ai répondu sur StackExchange .
Voici deux copies de Mario - les deux se déplacent sur l'écran au même rythme (soit par les sprites se déplaçant à droite dans le monde, soit par la caméra à gauche - cela finit par être équivalent), mais seule celle du haut montre ces artefacts ondulants et le celui du bas ne fait pas:
La raison en est que j'ai légèrement mis à l'échelle le Mario supérieur par un facteur de 1,01x - cette fraction minuscule supplémentaire signifie qu'il ne s'aligne plus avec la grille de pixels de l'écran.
Un petit désalignement translationnel n'est pas un problème - le filtrage de texture "le plus proche" se fixera de toute façon sur le texel le plus proche sans que nous ne fassions rien de spécial.
Mais un décalage d' échelle signifie que cette capture n'est pas toujours dans la même direction. À un endroit, un pixel recherchant le texel le plus proche en choisira un légèrement à droite, tandis qu'un autre pixel en choisira un légèrement à gauche - et maintenant une colonne de texels a été omise ou dupliquée entre les deux, créant une ondulation ou un chatoiement qui se déplace sur l'image-objet lorsqu'elle se déplace sur l'écran.
Voici un examen plus approfondi. J'ai animé un sprite champignon translucide se déplaçant en douceur, comme si nous pouvions le rendre avec une résolution de sous-pixels illimitée. Ensuite, j'ai superposé une grille de pixels en utilisant l'échantillonnage du plus proche voisin. Le pixel entier change de couleur pour correspondre à la partie du sprite sous le point d'échantillonnage (le point au centre).
Mise à l'échelle 1: 1
Même si l'image-objet se déplace en douceur vers les coordonnées sous-pixel, son image rendue s'accroche toujours correctement à chaque fois qu'elle parcourt un pixel complet. Nous n'avons rien à faire de spécial pour que cela fonctionne.
1: 1.0625 Mise à l'échelle
Dans cet exemple, le sprite champignon 16x16 texel a été mis à l'échelle jusqu'à 17x17 pixels d'écran, et donc l'accrochage se produit différemment d'une partie de l'image à l'autre, créant l'ondulation ou la vague qui s'étire et l'écrase en se déplaçant.
Ainsi, l'astuce consiste à ajuster la taille / le champ de vision de votre caméra afin que les texels source de vos actifs correspondent à un nombre entier de pixels d'écran. Il n'est pas nécessaire que ce soit 1: 1 - tout nombre entier fonctionne très bien:
Mise à l'échelle 1: 3
Vous devrez le faire un peu différemment en fonction de votre résolution cible - une simple mise à l'échelle pour s'adapter à la fenêtre entraînera presque toujours une échelle fractionnaire. Une certaine quantité de rembourrage sur les bords de l'écran peut être nécessaire pour certaines résolutions.
la source
mapSprite
est de 100x100, leplayerSprite
est défini sur une taille de 1x1 et la fenêtre d'affichage est définie sur une taille de 32x20. Même tout changer en multiples de 16 ne semble pas résoudre le problème. Des idées?window size
est 1000x1000 (pixels),WORLD_WIDTH
xWORLD_HEIGHT
est 100x100,FillViewport
est 10x10,playerSprite
est 1x1. Malheureusement, le problème persiste.voici une petite liste de contrôle:
Et ne modifiez la position actuelle de la caméra que si la caméra doit bouger.
Si la position de la caméra visée est autre chose que transform.position - définissez transform.position (de votre caméra) sur "viser la position de la caméra".
Cela devrait résoudre votre problème. Sinon: dites-moi. Cependant, vous devriez également envisager de faire ces choses:
réglez «projection de la caméra» sur «ortographique» (pour votre problème y anormalement croissant) (à partir de maintenant, vous ne devez zoomer qu'avec le «champ de vision»)
régler la rotation de la caméra sur 90 ° - pas (0 ° ou 90 ° ou 180 °)
ne générez pas de mipmaps si vous ne savez pas si vous le devez.
utilisez une taille suffisante pour vos sprites et n'utilisez pas de filtre bilinéaire ou trilinéaire
Si 1 unité n'est pas une unité de pixels, sa résolution d'écran dépend peut-être. Avez-vous accès au champ de vision? En fonction de votre champ de vision: découvrez la résolution de 1 unité.
^ Et maintenant, si onePixel (largeur) et onePixel (hauteur) ne sont pas 1.0000f, déplacez-vous simplement dans ces unités vers la gauche et la droite avec votre appareil photo.
la source
OrthographicCamera
pour que j'espère que ça fonctionne comme ça. (Vous utilisez Unity, non?) 3-5) Je les utilise déjà dans mon jeu.Je ne connais pas libgdx mais j'ai rencontré des problèmes similaires ailleurs. Je pense que le problème réside dans le fait que vous utilisez lerp et potentiellement une erreur dans les valeurs à virgule flottante. Je pense qu'une solution possible à votre problème est de créer votre propre objet de localisation de caméra. Utilisez cet objet pour votre code de mouvement et mettez-le à jour en conséquence, puis définissez la position de la caméra sur la valeur souhaitée (entière?) La plus proche de votre objet de localisation.
la source
y
augmentation anormale) était également présent avant mon utilisationlerp
. Je l'ai ajouté hier pour que les gens puissent également voir l'autre problème, où la taille des sprites ne reste pas constante lorsque la caméra approche.