Pada dasarnya saya ingin menghentikan kamera dari bergerak dalam subpiksel, karena saya pikir ini mengarah ke sprite yang secara nyata mengubah dimensi mereka jika hanya sedikit. (Apakah ada istilah yang lebih baik untuk itu?) Perhatikan bahwa ini adalah game pixel-art di mana saya ingin memiliki grafik pixelated yang tajam. Inilah gif yang menunjukkan masalah:
Sekarang yang saya coba adalah ini: Pindahkan kamera, proyeksikan posisi saat ini (jadi itu koordinat layar) dan kemudian bulatkan atau putar ke int. Setelah itu konversikan kembali ke koordinat dunia dan gunakan itu sebagai posisi kamera baru. Sejauh yang saya tahu, ini harus mengunci kamera ke koordinat layar yang sebenarnya, bukan pecahannya.
Namun, karena suatu alasan, y
nilai posisi baru itu baru saja meledak. Dalam hitungan detik itu meningkat menjadi seperti 334756315000
.
Berikut ini adalah SSCCE (atau MCVE) berdasarkan kode di 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());
}
}
dan inilah yangsc_map.jpg
dan yangdungeon_guy.png
Saya juga tertarik untuk mempelajari tentang cara yang lebih sederhana dan / atau lebih baik untuk memperbaiki masalah ini.
Jawaban:
Masalah Anda tidak menggerakkan kamera dalam peningkatan piksel penuh. Rasio texel-ke-pixel Anda sedikit non-integer . Saya akan meminjam beberapa contoh dari pertanyaan serupa yang saya jawab di StackExchange .
Berikut adalah dua salinan Mario - keduanya bergerak melintasi layar dengan kecepatan yang sama (baik oleh sprite bergerak tepat di dunia, atau kamera bergerak ke kiri - berakhir setara), tetapi hanya yang teratas menunjukkan artefak beriak ini dan yang bawah tidak:
Alasannya adalah bahwa saya sedikit meningkatkan Mario atas dengan 1 faktor 1,01x - fraksi ekstra kecil itu berarti dia tidak lagi sejalan dengan kisi-kisi piksel layar.
Misalignment translasi kecil bukan masalah - pemfilteran tekstur "terdekat" akan tetap melekat pada texel terdekat tanpa kita melakukan sesuatu yang istimewa.
Tapi skala ketidakcocokan berarti gertakan ini tidak selalu dalam arah yang sama. Di satu tempat piksel mencari texel terdekat akan mengambil satu sedikit ke kanan, sementara piksel lain mengambil satu sedikit ke kiri - dan sekarang kolom texel telah dihilangkan atau digandakan di antara keduanya, menciptakan riak atau kilau yang bergerak di atas sprite saat bergerak melintasi layar.
Inilah yang lebih dekat. Saya telah menghidupkan sprite jamur tembus pandang yang bergerak dengan lancar, seolah-olah kita dapat membuatnya dengan resolusi sub-pixel yang tidak terbatas. Lalu saya overlay grid pixel menggunakan sampling tetangga terdekat. Seluruh piksel berubah warna agar sesuai dengan bagian sprite di bawah titik pengambilan sampel (titik di tengah).
Penskalaan 1: 1
Meskipun sprite bergerak dengan lancar ke koordinat sub-pixel, gambar yang dihasilkannya tetap terkunci dengan benar setiap kali ia berjalan piksel penuh. Kita tidak perlu melakukan sesuatu yang istimewa untuk membuat ini bekerja.
1: 1.0625 Penskalaan
Dalam contoh ini, sprite jamur texel 16x16 telah diskalakan hingga 17x17 piksel layar, sehingga pemotretan terjadi secara berbeda dari satu bagian gambar ke yang lain, menciptakan riak atau gelombang yang membentang & meremasnya saat bergerak.
Jadi, triknya adalah menyesuaikan ukuran kamera / bidang pandang Anda sehingga sumber aset Anda akan memetakan peta ke sejumlah integer piksel layar. Tidak harus 1: 1 - angka berapa pun bekerja dengan baik:
1: 3 Skala
Anda harus melakukan ini sedikit berbeda tergantung pada resolusi target Anda - hanya meningkatkan skala agar sesuai dengan jendela akan selalu menghasilkan skala fraksional. Beberapa jumlah padding di tepi layar mungkin diperlukan untuk resolusi tertentu.
sumber
mapSprite
100x100,playerSprite
diatur ke ukuran 1x1 dan viewport diatur ke ukuran 32x20. Bahkan mengubah segalanya menjadi kelipatan 16 tampaknya tidak menyelesaikan masalah. Ada ide?window size
adalah 1000x1000 (piksel),WORLD_WIDTH
xWORLD_HEIGHT
adalah 100x100,FillViewport
adalah 10x10,playerSprite
adalah 1x1. Sayangnya, masalahnya masih berlanjut.di sini daftar kecil:
Dan hanya mengubah posisi kamera saat ini jika kamera harus bergerak.
Jika posisi kamera tujuan adalah sesuatu yang lain dari transform.position - atur transform.position (dari kamera Anda) ke "arahkan posisi kamera".
ini harus menyelesaikan masalah Anda. Jika tidak: beri tahu saya. Namun Anda harus mempertimbangkan untuk melakukan hal-hal itu juga:
atur "proyeksi kamera" menjadi "ortografis" (untuk masalah y Anda yang meningkat secara abnormal) (mulai sekarang, Anda hanya perlu memperbesar dengan "Bidang Tampilan")
atur rotasi kamera dalam 90 ° - langkah (0 ° atau 90 ° atau 180 °)
jangan menghasilkan peta jalan jika Anda tidak tahu jika Anda harus.
gunakan ukuran yang cukup untuk sprite Anda dan jangan gunakan filter bilinear atau trilinear
Jika 1 unit bukan unit pixel mungkin tergantung pada resolusi layarnya. Apakah Anda memiliki akses ke bidang tampilan? Tergantung pada bidang pandang Anda: Cari tahu berapa resolusi 1 unit.
^ Dan sekarang jika onePixel (lebar) dan onePixel (tinggi) bukan 1,0000f, cukup pindahkan unit-unit itu ke kiri dan kanan dengan kamera Anda.
sumber
OrthographicCamera
sehingga semoga bekerja seperti itu. (Anda menggunakan Unity, kan?) 3-5) Saya sudah melakukan itu di permainan saya.Saya tidak terbiasa dengan libgdx tetapi mengalami masalah serupa di tempat lain. Saya pikir masalahnya terletak pada kenyataan bahwa Anda menggunakan lerp dan berpotensi kesalahan dalam nilai floating point. Saya pikir solusi yang mungkin untuk masalah Anda adalah membuat objek lokasi kamera Anda sendiri. Gunakan objek ini untuk kode gerakan Anda dan perbarui sesuai dan kemudian atur posisi kamera ke nilai yang diinginkan (integer?) Terdekat dari objek lokasi Anda.
sumber
y
peningkatan yang tidak normal) juga ada sebelum saya gunakanlerp
. Saya benar-benar menambahkannya kemarin sehingga orang-orang juga bisa melihat masalah lain, di mana ukuran sprite tidak konstan ketika kamera mendekat.