Mengapa saya harus memisahkan objek dari rendering?


11

Disclamer: Saya tahu apa pola sistem entitas dan saya tidak menggunakannya.

Saya telah membaca banyak tentang memisahkan objek dan rendering. Tentang fakta bahwa logika permainan harus independen dari mesin rendering yang mendasarinya. Itu semua bagus dan keren dan itu masuk akal, tetapi itu menyebabkan banyak kesakitan lainnya juga:

  • perlu untuk sinkronisasi antara objek logika dan objek rendering (salah satu yang membuat keadaan animasi, sprite dll)
  • perlu membuka objek logika kepada publik agar objek rendering membaca keadaan aktual objek logika (sering mengarahkan objek logika untuk dengan mudah mentransformasikannya menjadi objek pengambil dan penyetel bodoh)

Ini kedengarannya bukan solusi yang baik untuk saya. Di sisi lain, sangat intuitif untuk membayangkan objek sebagai representasi 3d (atau 2d) dan juga sangat mudah untuk dipelihara (dan mungkin jauh lebih dienkapsulasi juga).

Apakah ada cara untuk mempertahankan representasi grafis dan logika game yang digabungkan bersama (menghindari masalah sinkronisasi) tetapi memisahkan mesin rendering? Atau adakah cara untuk memisahkan logika game dan rendering yang tidak menyebabkan kelemahan di atas?

(mungkin dengan contoh, saya tidak pandai memahami pembicaraan abstrak)


1
Ini juga akan sangat membantu jika Anda memberikan contoh tentang apa yang Anda maksudkan ketika Anda mengatakan Anda tidak menggunakan pola sistem entitas, dan bagaimana Anda berpikir yang berhubungan dengan apakah Anda harus memisahkan masalah render dari masalah entitas / tidak. logika game.
michael.bartnett

@ michael.bartnett, saya tidak memisahkan objek dalam komponen-komponen kecil yang dapat digunakan kembali yang ditangani oleh sistem, cara sebagian besar implementasi pola lakukan. Kode saya lebih merupakan upaya untuk pola MVC. Tapi itu tidak terlalu penting karena pertanyaannya tidak tergantung pada kode apa pun (bahkan bahasa). Saya telah menempatkan disclamer karena saya tahu beberapa akan mencoba meyakinkan saya untuk menggunakan ECS, yang tampaknya menyembuhkan kanker. Dan, seperti yang Anda lihat, itu tetap terjadi.
Sepatu

Jawaban:


13

Misalkan Anda memiliki adegan yang terdiri dari dunia , pemain , dan bos. Oh, dan ini adalah permainan orang ketiga, jadi Anda juga memiliki kamera .

Jadi adegan Anda terlihat seperti ini:

class Scene {
    World* world
    Player* player
    Enemy* boss
    Camera* camera
}

(Setidaknya, itulah data dasar . Bagaimana Anda menyimpan data, terserah Anda.)

Anda hanya ingin memperbarui dan membuat adegan ketika Anda memainkan game, bukan ketika dijeda, atau di menu utama ... sehingga Anda melampirkannya ke status permainan!

State* gameState = new State();
gameState->addScene(scene);

Sekarang kondisi permainan Anda memiliki heboh. Selanjutnya, Anda ingin menjalankan logika di tempat kejadian, dan membuat adegan. Untuk logika, Anda hanya menjalankan fungsi pembaruan.

State::update(double delta) {
    scene->update(delta);
}

Dengan begitu Anda bisa menyimpan semua logika game di Scenekelas. Dan hanya untuk referensi, sistem komponen entitas dapat melakukannya seperti ini sebagai gantinya:

State::update(double delta) {
    physicsSystem->applyPhysics(scene);
}

Ngomong-ngomong, kini Anda berhasil memperbarui adegan Anda. Sekarang Anda ingin menampilkannya! Untuk itu kami melakukan sesuatu yang mirip dengan di atas:

State::render() {
    renderSystem->render(scene);
}

Ini dia. Sistem render membaca informasi dari tempat kejadian, dan menampilkan gambar yang sesuai. Sederhana, metode untuk rendering adegan mungkin terlihat seperti ini:

RenderSystem::renderScene(Scene* scene) {
    Camera* camera = scene->camera;
    lookAt(camera); // Set up the appropriate viewing matrices based on 
                    // the camera location and direction

    renderHeightmap(scene->getWorld()->getHeightMap()); // Just as an example, you might
                                                        // use a height map as your world
                                                        // representation.
    renderModel(scene->getPlayer()->getType()); // getType() will return, for example "orc"
                                                // or "human"

    renderModel(scene->getBoss()->getType());
}

Sangat sederhana, Anda masih perlu, misalnya, menerapkan rotasi dan terjemahan berdasarkan di mana pemain Anda berada dan di mana dia melihat. (Contoh saya adalah game 3D, jika Anda menggunakan 2D, itu akan menjadi jalan-jalan di taman).

Saya harap ini yang Anda cari? Seperti yang mudah-mudahan dapat Anda ingat dari hal di atas, sistem render tidak peduli dengan logika permainan . Itu hanya menggunakan keadaan saat ini adegan untuk membuat, yaitu itu menarik informasi yang diperlukan darinya, untuk membuat. Dan logika game? Tidak peduli apa yang dilakukan penyaji. Heck, tidak peduli apakah itu ditampilkan sama sekali!

Dan Anda tidak perlu melampirkan informasi render ke tempat kejadian juga. Seharusnya cukup bahwa pemberi render tahu perlu membuat sebuah orc. Anda sudah memuat model orc, yang kemudian ditampilkan oleh renderer untuk ditampilkan.

Ini harus memenuhi persyaratan Anda. Representasi grafis dan logika digabungkan , karena keduanya menggunakan data yang sama. Namun mereka terpisah , karena tidak ada yang bergantung pada yang lain!

EDIT: Dan hanya untuk menjawab mengapa orang melakukannya seperti ini? Karena lebih mudah adalah alasan paling sederhana. Anda tidak perlu memikirkan "ini dan itu terjadi, sekarang saya harus memperbarui grafik". Alih-alih Anda membuat hal-hal terjadi, dan setiap bingkai permainan melihat apa yang sedang terjadi, dan menafsirkannya dengan cara tertentu, memberi Anda hasil di layar.


7

Judul Anda menanyakan pertanyaan yang berbeda dari konten tubuh Anda. Dalam judul, Anda bertanya mengapa logika dan rendering harus dipisahkan, tetapi di dalam tubuh Anda meminta implementasi sistem logika / grafik / rendering.

Pertanyaan kedua telah diatasi sebelumnya , jadi saya akan fokus pada pertanyaan pertama.

Alasan untuk memisahkan logika dan rendering:

  1. Anggapan luas bahwa benda harus melakukan satu hal
  2. Bagaimana jika Anda ingin beralih dari 2D ke 3D? Bagaimana jika Anda memutuskan untuk beralih dari satu sistem render ke sistem lainnya di tengah proyek? Anda tidak ingin menjelajahi semua kode Anda dan membuat perubahan besar di tengah logika permainan Anda.
  3. Anda mungkin memiliki alasan untuk mengulangi bagian kode, yang umumnya dianggap sebagai ide yang buruk.
  4. Anda dapat membangun sistem untuk mengontrol petak rendering atau logika yang berpotensi besar tanpa berkomunikasi secara individu dengan bagian-bagian kecil.
  5. Bagaimana jika Anda ingin menetapkan permata untuk pemain tetapi sistem diperlambat oleh berapa banyak aspek yang dimiliki permata? Jika Anda telah mengabstraksi sistem rendering Anda dengan cukup baik, Anda dapat memperbaruinya dengan tarif berbeda untuk memperhitungkan operasi rendering yang mahal.
  6. Ini memungkinkan Anda untuk memikirkan hal-hal yang benar-benar penting bagi apa yang Anda lakukan. Mengapa memutar otak Anda di sekitar transformasi matriks dan sprite offset dan koordinat layar ketika semua yang ingin Anda lakukan adalah menerapkan mekanik lompat ganda, menggambar kartu, atau memperlengkapi pedang? Anda tidak ingin sprite mewakili pedang lengkap Anda yang berwarna pink cerah hanya karena Anda ingin memindahkannya dari tangan kanan ke kiri.

Dalam pengaturan OOP instantiating objek baru memiliki biaya, tetapi dalam pengalaman saya biaya untuk sumber daya sistem adalah harga kecil untuk membayar kemampuan untuk memikirkan dan mengimplementasikan hal-hal spesifik yang perlu saya lakukan.


6

Jawaban ini hanya untuk membangun intuisi mengapa pemisahan rendering dan logika itu penting, daripada langsung menyarankan contoh-contoh praktis.

Anggaplah kita memiliki seekor gajah besar , tidak ada seorang pun di ruangan itu yang dapat melihat seluruh gajah. mungkin semua orang bahkan tidak setuju tentang apa itu sebenarnya. Karena semua orang melihat bagian yang berbeda dari gajah dan hanya bisa berurusan dengan bagian itu. Tetapi pada akhirnya ini tidak mengubah fakta bahwa itu adalah gajah yang besar.

Gajah mewakili objek permainan dengan semua perinciannya. Tapi tidak ada yang benar-benar perlu tahu segalanya tentang gajah (objek game) untuk dapat melakukan fungsinya.

Menggabungkan logika game dan rendering sebenarnya seperti membuat semua orang melihat seluruh gajah. Jika sesuatu berubah, semua orang perlu mengetahuinya. Sementara dalam kebanyakan kasus mereka hanya perlu melihat bagian yang mereka hanya tertarik. Jika sesuatu mengubah orang yang tahu tentang hal itu, hanya perlu memberi tahu orang lain tentang hasil dari perubahan itu, itulah yang hanya penting baginya (anggap ini sebagai komunikasi melalui pesan atau antarmuka).

masukkan deskripsi gambar di sini

Poin yang Anda sebutkan bukan drawback, mereka hanya drawback jika ada lebih banyak ketergantungan daripada yang seharusnya ada di mesin, dengan kata lain, sistem melihat bagian gajah lebih dari yang seharusnya. Dan ini berarti mesinnya tidak dirancang dengan "benar".

Anda hanya perlu sinkronisasi dengan definisi formal itu jika Anda menggunakan mesin multi-threaded di mana ia menempatkan logika dan rendering dalam dua utas yang berbeda, dan bahkan mesin yang membutuhkan banyak sinkronisasi antara sistem tidak terlalu banyak dirancang.

Kalau tidak, cara alami untuk menangani kasus tersebut, adalah merancang sistem sebagai input / output. Pembaruan melakukan logika dan menampilkan hasilnya. Rendering hanya umpan dengan hasil dari pembaruan. Anda tidak perlu mengungkapkan semuanya. Anda hanya mengekspos Antarmuka yang berkomunikasi antara dua tahap. Komunikasi antara berbagai bagian mesin harus melalui abstraksi (antarmuka) dan / atau pesan. Tidak ada logika atau keadaan internal yang harus diekspos.

Mari kita ambil contoh grafik adegan sederhana untuk menjelaskan ide tersebut.

Pembaruan biasanya dilakukan melalui satu loop yang disebut loop game (atau mungkin melalui beberapa loop game, masing-masing berjalan di utas terpisah). Setelah loop diperbarui objek game yang pernah ada. Hanya perlu memberi tahu melalui pesan atau antarmuka yang objek 1 dan 2 di mana diperbarui dan memberi makan dengan transformasi akhir.

Sistem rendering hanya mengambil transformasi akhir dan tidak tahu apa yang sebenarnya berubah tentang objek (misalnya tabrakan spesifik terjadi dll). Sekarang untuk membuat objek itu hanya perlu ID objek itu dan transformasi akhir. Setelah itu pemberi render akan memberi makan api rendering dengan mesh dan transformasi akhir tanpa mengetahui hal lain.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.