Mekanisme yang mendasari dalam 'hasil pengembalian www' dari Unity3D Game Engine

14

Di mesin game Unity3D, urutan kode umum untuk mendapatkan data jarak jauh adalah ini:

WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;

Apa mekanisme yang mendasarinya di sini?

Saya tahu kami menggunakan mekanisme hasil untuk memungkinkan frame berikutnya diproses, sementara unduhan sedang diselesaikan. Tetapi apa yang terjadi di bawah tenda ketika kita melakukan itu yield return www?

Metode apa yang dipanggil (jika ada, di kelas WWW)? Apakah Unity menggunakan utas? Apakah "Unity" lapisan mendapatkan contoh www dan melakukan sesuatu?

EDIT:

  • Pertanyaan ini khusus tentang internal Unity3D. Saya tidak tertarik dengan penjelasan tentang bagaimana yieldpernyataan bekerja di C #. Sebagai gantinya, saya mencari pandangan orang dalam tentang bagaimana Unity menangani konstruksi ini, untuk memungkinkan, misalnya, ke WWW untuk mengunduh sepotong data secara didistribusikan di beberapa bingkai.
thyandrecardoso
sumber
1
Perhatikan bahwa menggunakan yield returnuntuk operasi asinkron adalah hack. Dalam program C # "nyata", Anda akan menggunakan a Taskuntuk ini. Unity mungkin tidak menggunakan mereka karena itu dibuat sebelum. Net 4.0, ketika Taskdiperkenalkan.
BlueRaja - Danny Pflughoeft

Jawaban:

8

Ini adalah kata kunci C # hasilkan dalam tindakan - tidak melakukan sesuatu yang khusus dengan wwwobjek, melainkan berarti sesuatu yang istimewa untuk metode yang terdapat di dalamnya. Secara khusus kata kunci ini hanya dapat digunakan dalam metode yang mengembalikan IEnumerable(atau IEnumerator), dan digunakan untuk menunjukkan objek apa yang akan "dikembalikan" oleh enumerator ketika MoveNext dipanggil.

Ini bekerja karena kompiler mengubah seluruh metode menjadi kelas terpisah yang mengimplementasikan IEnumerable(atau IEnumerator) menggunakan mesin negara - hasil akhirnya adalah bahwa tubuh metode itu sendiri tidak dieksekusi sampai seseorang menghitung melalui nilai pengembalian. Ini akan bekerja dengan jenis apa pun, sama sekali tidak ada yang istimewa WWW, melainkan metode yang mengandung yang khusus.

Lihatlah di balik layar dari kata kunci C # yield untuk beberapa wawasan lebih lanjut tentang jenis kode apa yang dihasilkan oleh kompiler C #, atau hanya bereksperimen dan periksa kodenya sendiri menggunakan sesuatu seperti IL Spy


Pembaruan: Untuk memperjelas

  • Ketika Unity memanggil coroutine yang berisi yield returnpernyataan, semua yang terjadi adalah bahwa enumerator dikembalikan - tidak ada satupun metode yang dieksekusi pada titik ini
  • Untuk mendapatkan tubuh metode untuk mengeksekusi Unity harus memanggil MoveNextiterator untuk mendapatkan nilai pertama dalam urutan. Hal ini menyebabkan metode untuk mengeksekusi hingga yeild returnpernyataan pertama , di mana penelepon melanjutkan (dan mungkin Unity melanjutkan untuk membuat sisa frame)
  • Seperti yang saya pahami, Unity biasanya kemudian memanggil MoveNextmetode pada iterator sekali setiap frame berikutnya, menyebabkan metode untuk mengeksekusi kembali hingga yield returnpernyataan berikutnya sekali setiap frame, sampai akhir metode atau yield breakpernyataan tercapai (menunjukkan akhir urutan)

Bit hanya khusus disini (dan dalam beberapa dari kasus lain ) adalah bahwa Unity tidak memajukan iterator tertentu frame berikutnya, bukan hanya memajukan iterator (menyebabkan metode untuk terus mengeksekusi) ketika download selesai. Meskipun tampaknya ada kelas YieldInstruction dasar yang mungkin mengandung mekanisme umum untuk pensinyalan ke Unity ketika iterator harus ditingkatkan, WWWkelas tersebut tampaknya tidak mewarisi dari kelas ini sehingga saya hanya dapat berasumsi bahwa ada kasus khusus untuk kelas ini di mesin Unity.

Hanya untuk memperjelas - yieldkata kunci tidak melakukan sesuatu yang istimewa untuk WWWkelas, melainkan penanganan khusus yang diberikan Unity kepada anggota enumerasi yang dikembalikan yang menyebabkan perilaku ini.


Perbarui yang kedua: Adapun mekanisme yang WWWdigunakan untuk mengunduh halaman web secara serempak mungkin menggunakan Metode HttpWebRequest.BeginGetResponse yang secara internal akan menggunakan IO yang tidak sinkron atau sebagai alternatifnya dapat menggunakan utas (baik membuat utas khusus atau dengan menggunakan utas thread).

Justin
sumber
5
Sebenarnya, dalam Unity, sesuatu yang istimewa terjadi pada suatu WWWobjek ketika ia dihasilkan, lihat WWWreferensi .
Eric
9

yieldtampaknya terutama digunakan dalam Persatuan dalam konteks coroutine. Untuk membaca lebih lanjut tentang coroutine dan mengapa mereka menggunakan C #, yieldsaya merekomendasikan artikel blog ini: Unity3D coroutine secara rinci . Sebagian besar penelitian dalam jawaban ini berasal dari artikel itu.

Coroutine in Unity digunakan untuk merangkum tugas-tugas yang:

  1. Dapat memakan waktu lebih lama dari satu frame untuk ditampilkan (dengan demikian menyebabkan perlambatan), dan
  2. Dapat dilakukan secara terpisah dari loop game (karena hasilnya tidak perlu tersedia untuk frame saat ini).

Contoh dari jenis tugas ini adalah merintis jalan (kembali) perhitungan atau, seperti halnya dalam pertanyaan Anda, mendapatkan data dari situs web.

Untuk menjawab pertanyaan Anda (dalam urutan yang sedikit dimodifikasi):

Metode apa yang dipanggil (jika ada, di kelas WWW)? Apakah "Unity" lapisan mendapatkan contoh www dan melakukan sesuatu?

WWWKelas Unity dirancang untuk dihasilkan dari coroutine. Menurut komentar pada artikel blog yang ditautkan di atas, blok spekulatif kode (lapisan "atas") tentang YieldInstructions sebenarnya berisi saklar yang juga memeriksa WWWs yang dihasilkan . Kode ini kemudian memastikan coroutine akan selesai secara otomatis ketika unduhan selesai, seperti dijelaskan dalam WWWreferensi .

Apakah Unity menggunakan utas?

Dalam hal ini, untuk mengunduh data "tanpa memblokir sisa permainan": ya, kemungkinan besar. (Dan threading pasti digunakan untuk mendekompres data yang diunduh, seperti dibuktikan oleh WWW.threadPriority.)

Eric
sumber
bagus! Saya melihat komentar altdevblogaday.com/2011/07/07/unity3d-coroutines-in-detail/… dan tampaknya WWW diperlakukan secara khusus. Jadi saya ingin tahu bagaimana hal ini dilakukan, untuk memungkinkan pengunduhan dilakukan di beberapa frame, tanpa menggunakan utas?
thyandrecardoso
Poin bagus! Saya kira harus ada beberapa threading yang terjadi pada tingkat itu, saya akan mengedit jawaban saya untuk mencerminkan itu.
Eric
1
@ thyandrecardoso Saya kira itu menggunakan HttpWebRequest.BeginGetResponse atau serupa, namun Anda bisa mendekompilasi majelis untuk mengkonfirmasi ini jika benar-benar penting bagi Anda.
Justin
untuk saat ini, saya benar-benar hanya menunggu "penjelasan terbaik" ... akan lebih bagus jika siapa pun dari tim Unity dev memberikan "jawaban yang benar" 8 -) ... pada akhirnya, saya pikir implementasi nyata tidak akan jauh dari yang sudah diberikan di sini ... Saya tidak memiliki kebutuhan aktual untuk mendekompilasi majelis dan mengetahui semua ini dengan pasti. Tapi saya mungkin mencobanya nanti :)
thyandrecardoso
7

Sayangnya, WWW diimplementasikan secara internal sebagai kode asli, artinya kita tidak dapat melihat kode tersebut. Dari eksperimen saya bisa mengatakan itu

  1. WWWadalah tidak berasal dari YieldInstruction, sehingga apa pun yang terjadi ketika Anda yieldharus ditangani oleh kode-kasus khusus.
  2. Saya tidak pernah melihat perbedaan antara keduanya

    yield return www;

    dan

    while(!www.isDone)
        yield return null;

    Saya pikir itu cara paling logis untuk mengimplementasikannya, dan kemungkinan besar itulah yang terjadi di bawah tenda. Tapi saya tidak tahu pasti.

  3. Unity tidak memulai utas baru untuk mengunduh, setidaknya pada beberapa platform (iOS, webplayer). Atau jika ya, ini akan diatur WWW.isDonepada utas utama. Saya tahu ini karena kode ini:

    while(!www.isDone)
        Thread.Sleep(500);

    tidak bekerja

Saya tidak berpikir Anda dapat memiliki jawaban yang lebih spesifik kecuali seseorang dengan akses ke kode sumber Unity3d datang ke sini.

Lupakan
sumber
Ya. Wawasan yang sangat bagus! Terima kasih! Meskipun tidak meluncurkan utas per se, hal yang paling memungkinkan yang mungkin terjadi adalah WWW (atau lapisan mesin) menggunakan HttpWebRequest.BeginGetResponse (atau sesuatu seperti itu) ... benar? Sesuatu yang sepenuhnya async harus terjadi dengan cara apa pun ... unduhan tidak dapat "dijeda".
thyandrecardoso
** tidak bisa "dijeda" di antara frame, maksud saya.
thyandrecardoso
2

Karena Unity3D menggunakan C # sebagai mesin scripting mereka saya kira kata kunci hasil standar yang dibangun ke dalam C #. Pada dasarnya apa yang dimaksud adalah bahwa ia mengembalikan nilai www sehingga Anda dapat melanjutkan sementara iterasi berikutnya akan mengembalikan nilai berikutnya, dll ... Yield pada dasarnya menciptakan mesin negara dan iterator di latar belakang.

Roy T.
sumber
Ya, saya pikir saya memiliki gagasan dasar tentang kata kunci hasil. Namun, apa yang terjadi selama konstruktor WWW yang memungkinkan untuk "mesin negara" itu? Maksud saya, "menghasilkan pengembalian www" tampaknya tidak memanggil apa pun di dalam kelas WWW ... Ketika Anda mengatakan "itu berarti bahwa ia sudah mengembalikan nilai www", berapakah nilai instance www? Apa yang akan dilakukan oleh instance itu pada "iterasi" berikutnya?
thyandrecardoso
1
Di Unity coroutine, menghasilkan WWW adalah kasus khusus, lihat WWWreferensi . Selain itu, saya tidak yakin yieldmenciptakan apa pun. Konteks iterator dibuat dengan menerapkan IEnumerableatau menggunakannya sebagai tipe kembali. "Mesin negara" tampaknya mati juga. Tentu, ada keadaan, tetapi itu saja bukan properti yang memadai, bukan? Mungkin Anda bisa menguraikannya.
Eric
1
Berikut ini adalah referensi yang baik tentang perilaku C # yang normal. shadowcoding.blogspot.nl/2009/01/yield-and-c-state-machine.html . Yield menghasilkan banyak kode untuk melacak keberadaannya di iterator. Tentang kasus khusus Unity yang menghasilkan WWW, saya tidak tahu, tetapi menurut dokumen itu tidak ada hubungannya dengan kata kunci C # normal, itu sangat membingungkan, mereka tidak bisa hanya menjadikannya metode async.
Roy T.