Kami melanjutkan percakapan tentang penembak 3D selama akhir pekan. Jika ada, maka saya mengingatkan Anda bahwa ini adalah babak kedua:
Seperti yang saya katakan, saya melakukan yang terbaik untuk mendukung keinginan siswa untuk melakukan sesuatu dengan tangan mereka sendiri. Secara khusus, ketika saya memberikan kuliah tentang pengantar pemrograman, maka sebagai latihan praktis saya meninggalkan mereka hampir sepenuhnya kebebasan. Hanya ada dua batasan: bahasa pemrograman (C ++) dan tema proyek, ini harus berupa video game. Berikut adalah contoh dari salah satu dari ratusan permainan yang dibuat oleh mahasiswa baru saya:
Sayangnya, sebagian besar siswa memilih game sederhana seperti platformer 2D. Saya menulis artikel ini untuk menunjukkan bahwa menciptakan ilusi dunia tiga dimensi tidak lebih sulit daripada mengkloning mario broz.
Saya ingatkan Anda bahwa kami berhenti di panggung yang memungkinkan Anda untuk memberi tekstur pada dinding:

Tahap 13: menggambar monster di peta
Apa itu monster dalam game kita? Ini adalah koordinat dan jumlah teksturnya:
struct Sprite { float x, y; size_t tex_id; }; [..] std::vector<Sprite> sprites{ {1.834, 8.765, 0}, {5.323, 5.365, 1}, {4.123, 10.265, 1} };
Setelah menetapkan beberapa monster, untuk permulaan kita cukup menggambar mereka di peta:

Perubahannya
bisa Anda lihat di sini .

Tahap 14: kotak hitam bukannya monster dalam 3D
Sekarang kita akan menggambar sprite di jendela 3D. Untuk melakukan ini, kita perlu menentukan dua hal: posisi sprite pada layar dan ukurannya. Berikut adalah fungsi yang menggambar kotak hitam di tempat masing-masing sprite:
void draw_sprite(Sprite &sprite, FrameBuffer &fb, Player &player, Texture &tex_sprites) {
Mari kita cari tahu cara kerjanya. Berikut ini diagramnya:

Di baris pertama, kami mempertimbangkan sprite_dir sudut absolut (sudut antara arah dari pemain ke sprite dan absis). Sudut relatif antara sprite dan arah pandangan jelas diperoleh dengan hanya mengurangkan dua sudut absolut: sprite_dir - player.a. Jarak dari pemain ke sprite adalah sepele untuk dihitung, dan ukuran sprite adalah pembagian sederhana dari ukuran layar berdasarkan jarak. Nah, untuk jaga-jaga, saya memotong dua ribu dari atas agar tidak mendapatkan kotak raksasa (omong-omong, kode ini dapat dengan mudah dibagi dengan nol). h_offset dan v_offset memberikan koordinat sudut kiri atas sprite di layar; kemudian loop ganda sederhana mengisi kotak kami dengan warna hitam. Periksa dengan pena dan selembar kertas yang h_offset dan v_offset dihitung dengan benar, di komit saya ada kesalahan (tidak kritis), percaya kode dalam artikel :) Yah, kode yang lebih baru di repositori juga telah diperbaiki.

Perubahannya
bisa Anda lihat di sini .

Langkah 15: Kedalaman Peta
Kotak kami secara ajaib bagus, tetapi hanya ada satu masalah: monster yang jauh mengintip di sudut, dan alun-alun ditarik seluruhnya. Bagaimana menjadi Sangat sederhana. Kami menggambar sprite
setelah dinding digambar. Karena itu, untuk setiap kolom layar kita, kita tahu jarak ke dinding terdekat. Kami menyimpan jarak ini ke array dengan nilai 512, dan meneruskan array ke fungsi rendering sprite. Sprite juga digambar kolom demi kolom, jadi untuk setiap kolom sprite kita akan membandingkan jaraknya dengan nilai dari array kedalaman kita.

Perubahannya
bisa Anda lihat di sini .

Tahap 16: masalah dengan sprite
Mereka menjadi monster yang hebat, bukan? Tetapi pada tahap ini saya tidak akan menambahkan fungsionalitas, sebaliknya, saya akan menghancurkan semuanya dengan menambahkan monster lain:

Perubahannya
bisa Anda lihat di sini .

Tahap 17: memilah sprite
Apa masalahnya? Masalahnya adalah bahwa saya dapat memiliki urutan sewenang-wenang menggambar sprite, dan untuk masing-masing saya membandingkan jaraknya dengan dinding, tetapi tidak dengan sprite lain, sehingga makhluk jauh merangkak keluar dari yang terdekat. Apakah mungkin untuk mengadaptasi solusi dengan peta mendalam untuk menggambar sprite?
Teks tersembunyiJawaban yang benar adalah "Anda bisa." Tapi bagaimana caranya? Tulis di komentar.
Saya akan pergi ke arah lain, memecahkan masalah dengan bodoh di dahi. Saya hanya akan menggambar semua sprite dari yang terjauh ke terjauh. Artinya, saya akan mengurutkan sprite dalam urutan jarak, dan menggambar mereka dalam urutan itu.

Perubahannya
bisa Anda lihat di sini .

Langkah 18: Waktu SDL
Waktunya telah tiba untuk SDL. Ada banyak perpustakaan jendela lintas-platform yang berbeda, dan saya tidak mengerti sama sekali. Secara pribadi, saya suka
imgui , tetapi karena alasan tertentu siswa saya lebih suka SDL, jadi saya terhubung dengannya. Tugas untuk tahap ini sangat sederhana: membuat jendela dan menampilkan gambar dari tahap sebelumnya:

Perubahannya
bisa Anda lihat di sini . Saya tidak memberikan tautan ke gitpod lagi, karena SDL di browser belum belajar memulai :(
Pembaruan: BELAJAR! Anda dapat menjalankan kode dalam satu klik di browser!
Langkah 19: Pemrosesan dan Pembersihan Acara
Tambahkan reaksi pada penekanan tombol bahkan tidak lucu, saya tidak akan menjelaskan. Saat menambahkan SDL, saya menghapus ketergantungan pada stb_image.h. Itu indah, tetapi butuh waktu terlalu lama untuk dikompilasi.
Bagi mereka yang tidak mengerti, sumber-sumber dari tahap kesembilan belas
ada di sini . Nah, berikut ini adalah kinerja yang khas:
Kesimpulan
Kode saya saat ini hanya berisi 486 baris, dan pada saat yang sama saya tidak menyimpannya sama sekali:
haqreu@daffodil:~/tinyraycaster$ cat *.cpp *.h | wc -l 486
Saya tidak menjilat kode saya, dengan sengaja membuang cucian kotor. Ya, saya menulis seperti itu (dan bukan hanya saya). Suatu Sabtu pagi saya hanya duduk dan menulis ini :)
Saya tidak membuat game jadi, tugas saya hanya memberikan dorongan awal untuk penerbangan imajinasi Anda. Tulis kode Anda sendiri, mungkin akan lebih baik dari saya. Bagikan kode Anda, bagikan ide-ide Anda, kirim permintaan tarik.