Cara menggambar garis di sekitar sprite yang efisien

21

Saya menggunakan XNA untuk memprogram permainan, dan telah bereksperimen dengan berbagai cara untuk mencapai efek 'terpilih' pada sprite saya. Masalah yang saya alami adalah bahwa setiap klik yang ditarik dalam spritebatch diambil menggunakan lebih dari satu sprite tunggal (setiap objek dapat terdiri hingga 6 sprite).

Saya akan menghargai itu jika seseorang bisa memberi tahu saya tentang bagaimana saya bisa mencapai menambahkan garis besar ke sprite X piksel saya (sehingga lebar garis dapat berbagai jumlah seluruh piksel).

Terima kasih sebelumnya,

  • Greg.
Greg
sumber

Jawaban:

20

Sejauh ini cara termudah untuk melakukan ini (jadi mungkin cara terbaik, kecuali jika Anda benar-benar kekurangan kinerja) adalah memiliki dua salinan sprite Anda.

  • Versi reguler
  • Versi "gemuk", tidak berwarna - pada dasarnya versi putih dari sprite X Anda - lebih banyak piksel "lebih gemuk" dari aslinya.

Gambarkan seluruh objek Anda menggunakan versi "gendut", lalu gambarkan versi reguler di atasnya.

Dengan menjadikan versi "gemuk" putih, Anda dapat menggunakan pewarnaan warna bawaan SpriteBatch untuk mengubah warna pilihan secara dinamis.

Untuk menghasilkan verison "gemuk" Anda, saya sarankan untuk menulis Content Pipeline Extension yang dapat secara otomatis mengambil sprite asli Anda, membaca saluran alfa mereka, membuat saluran alfa baru dengan mengambil sampel saluran alfa maksimum dalam gambar asli X-banyak piksel di sekitar setiap piksel, dan pengaturan RGB = (1,1,1).

Anda harus memastikan semua sprite Anda memiliki batas transparan yang cukup untuk menambahkan garis besar (Anda dapat memeriksa ini di pengolah konten - dan bahkan memberikan ruang jika perlu).

Jika Anda hanya memiliki beberapa sprite, maka Anda bisa menggunakan editor gambar yang baik (GIMP, Photoshop) dan melakukannya dengan tangan: saluran Alpha untuk memilih, memperluas pilihan, memilih ke alpha, mengisi saluran warna putih.

Andrew Russell
sumber
4

Saya kira Anda perlu menggambar semua bagian yang masing-masing objek menjadi satu sprite terlebih dahulu. Maka saya pikir Anda harus menulis shader untuk mendeteksi tepi sprite menggambar pixel mana pun ia menemukan tepi. Saya berharap harus ada beberapa shader di luar sana untuk melakukan ini, yang bisa Anda gunakan atau port.

Iain
sumber
11
Jenis shader ini sangat mengganggu untuk ditulis, saya pernah mendengarnya (kami menggunakannya di salah satu game 3D kami dan terus berlari ke case edge yang tidak sedap dipandang). Satu hal yang mungkin ingin Anda pertimbangkan adalah meng-encode outline langsung ke dalam tekstur sprite dengan warna tertentu seperti (0, 255, 255, 0), dan biarkan saja shader mengubah warna itu ketika sprite dipilih. Maka shader adalah perubahan warna sepele, dan Anda memiliki kontrol artis tingkat tinggi atas garis besar dan detail apa pun yang Anda inginkan.
4

Bergantung pada persyaratan, yang mungkin juga efektif hanyalah membuat garis besar sesuai permintaan untuk sprite. Saya berasumsi sprite Anda memiliki transparansi, dan bentuknya tidak beraturan daripada hanya menjadi persegi panjang (sementara ini akan bekerja dengan baik untuk itu, garis besar persegi panjang harus Trivial).

when selected:
   outline = new sprite canvas of appropriate size
   for sprite in object:
      # use larger numbers for thicker outlines
      for x in (-1, 0, 1) and y in (-1, 0, 1):
         render sprite mask into canvas at x,y with desired color

Perhatikan bahwa Anda tidak perlu melakukan ini setiap undian (meskipun saya kira Anda bisa), tetapi cukup buat sprite garis baru saat mengganti sprite.

dash-tom-bang
sumber
Ya, ini yang ingin saya sarankan juga. Jadikan sprite bertopeng 1 piksel ke kiri, kanan, atas dan bawah sprite asli, di bawah sprite yang ada. Banyak game menggunakan metode ini untuk menguraikan teks yang diberikan, atau dengan hanya 2 dari 4 posisi untuk membuat bayangan.
Kaj
1

Pendekatan brute force yang paling sederhana adalah membangun dua salinan dari masing-masing sprite, yang normal dan yang disorot. Kemudian cukup tukar ketika disorot.

Jika Anda punya memori cadangan, tidak perlu menjadi lebih rumit dari itu. Plus seniman memiliki kendali penuh atas tampilan ketika disorot sehingga Anda dapat melakukan garis besar atau apa pun yang Anda inginkan.

wkerslake
sumber
Saya pikir bagian dari masalahnya adalah bahwa OP mengatakan setiap objek dapat dibuat dari beberapa sprite. Jadi saya kira garis besar harus menjadi garis besar objek gabungan, alih-alih memiliki garis besar yang terpisah di sekitar setiap komponen sprite.
Chris Howe
1
Chris, itu tidak harus menjadi garis besar objek gabungan, dan itu akan berfungsi dengan baik untuk objek yang terbuat dari beberapa sprite. Lakukan persis apa yang dikatakan wkerslake, tetapi pastikan Anda menggambar sprite yang disorot di belakang semua sprite normal untuk objek itu. Dengan cara itu, tidak peduli berapa banyak sprite yang dibuat dari sebuah objek, highlight hanya akan menggunakan sedikit lebih dari dua kali waktu draw untuk objek itu, yang seharusnya jauh lebih sedikit daripada menghasilkan outline saat rendertime dengan sprite gabungan.
AttackingHobo
1

Bagaimana dengan untuk setiap sprite, juga memiliki sprite lain yang merupakan garis besar sprite dasar. Saat menggambar objek yang diuraikan, gambar sprite dasar, lalu buat topeng rendering gabungan, lalu gambar sprite garis luar termasuk topeng.

Abu-abu
sumber
2
Mengapa tidak menggambar sprite garis besar terlebih dahulu dan menggambar sprite reguler di atasnya?
Chris Howe
Salah satu alasan untuk tidak melakukan ini (atau saran Chris) adalah karena menggunakan memori tekstur dua kali lebih banyak; alasan lain adalah bahwa alur kerja artis adalah omong kosong karena Anda perlu memperbarui dua file setiap kali Anda mengubah sprite.
2
Ya, Anda idealnya tidak akan memiliki dua aset sprite yang sebenarnya. Jika Anda menggunakan pengujian alpha untuk sprite Anda, Anda bisa memasukkan garis besar ke dalam sprite dan memberinya alpha yang sedikit lebih tinggi dari area transparan Anda. Kemudian dengan mengendalikan nilai referensi uji alpha Anda dapat mengontrol apakah garis besar terlihat atau tidak. Yang saya katakan adalah bahwa jika Anda memiliki 2 versi sprite (apakah 2 aset atau 2 status aset yang sama), Anda harus menggambar versi garis besar terlebih dahulu, dan kemudian versi normal. Dengan cara ini Anda tidak memerlukan bayangan mewah, topeng, atau apa pun.
Chris Howe
1
Gagasan Chris memiliki kelebihan; selain itu memiliki artis memperbarui 2 file (atau bahkan aset yang sama) dapat dihindari jika Anda menghasilkan alat untuk menghasilkan alpha untuk sprite garis besar yang berjalan sebagai bagian dari pipa aset / konten Anda. Memori tekstur mungkin atau mungkin tidak menjadi masalah, tetapi sprite outline yang terpisah harus sangat kompres DXT; mungkin 1/6 ukuran tekstur asli dalam memori video dan tanpa kehilangan kesetiaan.
jpaver
1

Beberapa solusi berbeda dengan trade off yang berbeda.

Paling mudah: Merender objek menggunakan warna datar beberapa kali dan mengguncang posisi (mengimbangi kiri, atas, bawah, dll, dll), ini akan membuat versi garis besar apa pun yang Anda render di atasnya, tetapi memiliki biaya kinerja dan tidak akan memungkinkan untuk batas lemak tanpa banyak render tambahan. Batas satu atau dua piksel mungkin oke dengan 4x.

Tercepat: Memproses ulang tekstur dan memiliki salinan yang sudah dibatasi, atau hanya perbatasan, atau merupakan topeng 8-bit skala abu-abu datar yang dapat Anda warnai dalam shader. Ini kemungkinan akan cepat dengan biaya memori.

Terbaik: Pendapat saya, tetapi menghasilkan representasi Bidang Jarak Ditandatangani (SDF) objek Anda mungkin akan menjadi solusi terbaik. Tekstur ini bisa jauh lebih kecil dari tekstur sumber dan masih menangkap data yang berguna. Pada dasarnya setiap piksel mengkodekan seberapa jauh jaraknya dari objek yang digunakan untuk menghasilkannya. Dengan data ini di tangan, Anda dapat menulis semua jenis efek dari bersinar hingga garis besar. Perbatasan dapat berubah dalam ukuran dan warna dll, dan masih merupakan shader yang relatif murah dan hanya satu gambar tambahan. Downside adalah tooling, dan preprocessing.

Aku
sumber
0

Saya tidak yakin dengan efisiensi, tetapi cara termudah yang dapat saya lihat adalah menggambar versi sprite yang lebih besar dalam warna yang ingin Anda pilih terlebih dahulu. Gambarlah sprite di atasnya. Anda hanya akan melihat tepi sprite pertama, memberikan efek seleksi.

EDIT: Namun, seperti yang Anda lihat dari komentar, ini bukan ide yang baik.

Bebek Komunis
sumber
1
Hanya meningkatkan sprite tidak memberikan garis besar yang benar
Iain
2
Saya pikir itu tidak akan terjadi, tetapi itu akan mudah.
The Communist Duck
2
Banyak hal yang mudah tetapi tidak menyelesaikan masalah. Untuk sprite dengan jenis siluet yang menarik, peningkatan skala akan menghasilkan sampah absolut.
Menaikkan skala hanya akan terlihat bagus untuk benda berbentuk bujur sangkar atau lingkaran. Jika bentuknya benar-benar lebar, atau tinggi, atau memiliki celah akan terlihat sangat buruk.
AttackingHobo
3
Peningkatan akan memberikan hasil yang lebih baik daripada tidak melakukan apa pun dan juga akan menjadi langkah yang bermanfaat di sepanjang jalan menuju "kesempurnaan." Sementara hasil grafis tidak akan sempurna, jika ini untuk alat internal mungkin Cukup Baik.
dash-tom-bang
0

Saya setuju dengan meningkatkan sprite. Sejauh ini rute termudah, dan Anda dapat menerapkannya untuk memilih sprite APAPUN tanpa harus membuat sprite tambahan terutama untuk tujuan ini.

  • yaitu spriteOutline.Scale = spriteOriginal.Scale * 0.1f; spriteOutline.Color = Warna baru (255, 0, 0, 255);

sumber
0

Ganti warna sprite asli dengan warna outline (atau warnai jika Anda suka). Jadikan sprite ini diarsir atau diwarnai empat kali dengan offset 1 piksel: pada x, y = (- 1, -1), lalu (+ 1, -1), lalu (-1, + 1) lalu (+1 , +1). Ulangi untuk semua sprite yang membentuk objek.

Setelah itu, render sprite asli dalam urutan yang benar di atas pada (0,0).

Ilya Bossov
sumber