Saya memiliki beberapa kode yang hanya ingin saya jalankan sekali, meskipun keadaan yang memicu kode itu bisa terjadi beberapa kali.
Misalnya, ketika pengguna mengklik mouse, saya ingin mengekliknya:
void Update() {
if(mousebuttonpressed) {
ClickTheThing(); // I only want this to happen on the first click,
// then never again.
}
}
Namun, dengan kode ini, setiap kali saya mengklik mouse, masalahnya akan diklik. Bagaimana saya bisa mewujudkannya hanya sekali?
architecture
events
MichaelHouse
sumber
sumber
Jawaban:
Gunakan bendera boolean.
Pada contoh yang ditampilkan, Anda akan memodifikasi kode menjadi sesuatu seperti berikut:
Lebih lanjut, jika Anda ingin dapat mengulangi tindakan, tetapi batasi frekuensi tindakan (yaitu waktu minimum antara setiap tindakan). Anda akan menggunakan pendekatan yang serupa, tetapi mengatur ulang bendera setelah waktu tertentu. Lihat jawaban saya di sini untuk lebih banyak ide tentang itu.
sumber
onStart() { delegate = doSomething; } ... if(mousebuttonpressed) { delegate.Execute(); }
danvoid doSomething() { doStuff(); delegate = funcDoNothing; }
tetapi pada akhirnya semua opsi akhirnya memiliki semacam bendera yang Anda atur / atur ... dan mendelegasikan dalam hal ini tidak lain (kecuali jika Anda memiliki lebih dari dua opsi apa yang harus dilakukan mungkin?).Jika bool flag tidak mencukupi atau Anda ingin meningkatkan keterbacaan * kode dalam
void Update()
metode, Anda dapat mempertimbangkan menggunakan delegasi (function pointer):Untuk delegasi "eksekusi sekali" yang sederhana terlalu banyak, jadi saya sarankan menggunakan bool flag saja.
Namun, jika Anda membutuhkan fungsionalitas yang lebih rumit, para delegasi mungkin merupakan pilihan yang lebih baik. Misalnya, jika Anda ingin melakukan tindakan rantai yang lebih berbeda: satu klik pertama, satu lagi klik kedua dan satu lagi ketiga Anda bisa lakukan:
alih-alih mengganggu kode Anda dengan puluhan flag berbeda.
* dengan biaya pemeliharaan lebih rendah dari sisa kode
Saya melakukan beberapa profil dengan hasil seperti yang saya harapkan:
Tes pertama dijalankan pada Unity 5.1.2, diukur dengan
System.Diagnostics.Stopwatch
pada proyek yang dibangun 32-bit (tidak dalam perancang!). Yang lain di Visual Studio 2015 (v140) dikompilasi dalam mode rilis 32-bit dengan / Ox flag. Kedua tes dijalankan pada Intel i5-4670K CPU @ 3.4GHz, dengan 10.000.000 iterasi untuk setiap implementasi. kode:kesimpulan: Sementara kompilator Unity melakukan pekerjaan dengan baik ketika mengoptimalkan pemanggilan fungsi, memberikan hasil yang kira-kira sama untuk flag positif dan delegasi (masing-masing 21 dan 25 ms) kesalahan prediksi cabang atau pemanggilan fungsi masih cukup mahal (catatan: delegasi harus dianggap sebagai cache dalam tes ini).
Menariknya, kompilator Unity tidak cukup pintar mengoptimalkan cabang ketika ada 99 juta kesalahan prediksi berturut-turut, sehingga meniadakan tes secara manual menghasilkan beberapa peningkatan kinerja yang memberikan hasil terbaik 5 ms. Versi C ++ tidak menunjukkan peningkatan kinerja untuk kondisi peniadaan, namun overhead panggilan fungsi keseluruhan secara signifikan lebih rendah.
yang paling penting: perbedaannya sangat tidak relevan untuk skenario dunia nyata
sumber
Untuk kelengkapan
(Sebenarnya tidak menyarankan Anda melakukan ini karena sebenarnya cukup mudah untuk hanya menulis sesuatu seperti
if(!already_called)
, tetapi akan "benar" untuk melakukannya.)Tidak mengherankan, standar C ++ 11 telah mengutarakan masalah yang agak sepele memanggil fungsi sekali, dan membuatnya sangat eksplisit:
}
Diakui, solusi standarnya adalah agak lebih unggul daripada yang sepele di hadapan utas karena masih menjamin bahwa selalu tepat satu panggilan terjadi, tidak pernah sesuatu yang berbeda.
Namun, Anda biasanya tidak menjalankan loop peristiwa multithreaded dan menekan tombol-tombol dari beberapa tikus pada saat yang sama, sehingga keamanan thread agak berlebihan untuk kasus Anda.
Dalam istilah praktis, itu berarti bahwa selain inisialisasi thread-safe dari boolean lokal (yang C ++ 11 menjamin untuk tetap aman thread), versi standar juga harus melakukan operasi test_set atom sebelum menelepon atau tidak memanggil fungsi.
Namun, jika Anda suka bersikap eksplisit, ada solusinya.
EDIT:
Hah. Sekarang saya telah duduk di sini, menatap kode itu selama beberapa menit, saya hampir cenderung merekomendasikannya.
Sebenarnya itu tidak sebodoh yang kelihatannya pada awalnya, memang sangat jelas dan jelas mengomunikasikan niat Anda ... bisa dibilang lebih terstruktur dan lebih mudah dibaca daripada solusi lain yang melibatkan
if
atau semacamnya.sumber
Beberapa saran akan bervariasi tergantung pada arsitektur.
Buat clickThisThing () sebagai fungsi pointer / varian / DLL / so / etc ... dan pastikan itu diinisialisasi ke fungsi yang Anda butuhkan ketika objek / komponen / modul / etc ... instantiated.
Di handler setiap kali tombol mouse ditekan lalu panggil pointer fungsi clickThisThing () dan kemudian segera ganti pointer dengan pointer fungsi NOP (no operation) lainnya.
Tidak perlu bendera dan logika tambahan untuk seseorang untuk mengacaukan nanti, panggil saja dan ganti setiap waktu dan karena itu NOP, NOP tidak melakukan apa-apa dan diganti dengan NOP.
Atau Anda bisa menggunakan bendera dan logika untuk melewati panggilan.
Atau Anda dapat memutuskan fungsi Pembaruan () setelah panggilan sehingga dunia luar melupakannya dan tidak pernah memanggilnya lagi.
Atau Anda memiliki clickThisThing () sendiri menggunakan salah satu dari konsep-konsep ini untuk hanya merespons dengan pekerjaan yang bermanfaat sekali. Dengan cara ini Anda dapat menggunakan objek / komponen / modul baseline clickThisThingOnce () dan instantiate di mana saja Anda memerlukan perilaku ini dan tidak ada dari updater Anda yang memerlukan logika khusus di semua tempat.
sumber
Gunakan pointer fungsi atau delegasi.
Variabel yang menahan fungsi pointer tidak perlu diperiksa secara eksplisit sampai perubahan perlu dilakukan. Dan ketika Anda tidak lagi perlu mengatakan logika untuk menjalankan, ganti dengan ref ke fungsi kosong / tidak-op. Bandingkan dengan melakukan persyaratan memeriksa setiap frame untuk seluruh masa hidup program Anda - bahkan jika bendera itu hanya diperlukan dalam beberapa frame pertama setelah startup! - Tidak bersih dan tidak efisien. (Poin Boreal tentang prediksi diakui dalam hal ini.)
Conditional bersarang, yang sering merupakan ini, bahkan lebih mahal daripada harus memeriksa satu kondisional setiap frame, karena prediksi cabang tidak dapat lagi melakukan keajaiban dalam kasus itu. Itu berarti penundaan teratur pada urutan 10-an siklus - warung pipa par excellence . Sarang dapat terjadi di atas atau di bawah bendera boolean ini. Saya tidak perlu mengingatkan pembaca tentang bagaimana loop permainan yang kompleks dapat dengan cepat menjadi.
Pointer fungsi ada karena alasan ini - gunakan!
sumber
Dalam beberapa bahasa skrip seperti javascript atau lua dapat dengan mudah dilakukan pengujian referensi fungsi. Dalam Lua ( love2d ):
sumber
Di komputer Arsitektur Von Neumann , memori adalah tempat instruksi dan data program Anda disimpan. Jika Anda ingin membuat kode hanya berjalan sekali, Anda bisa memiliki kode dalam metode menimpa metode dengan NOP setelah metode selesai. Dengan cara ini jika metode dijalankan lagi, tidak ada yang terjadi.
sumber
Dengan python jika Anda berencana menjadi orang brengsek bagi orang lain di proyek:
skrip ini menunjukkan kode:
sumber
Berikut kontribusi lain, ini sedikit lebih umum / dapat digunakan kembali, sedikit lebih mudah dibaca dan dipelihara, tetapi kemungkinan kurang efisien daripada solusi lain.
Mari merangkum logika sekali-eksekusi kami di kelas, Sekali:
Menggunakannya seperti contoh yang diberikan terlihat sebagai berikut:
sumber
Anda dapat mempertimbangkan menggunakan variabel statis.
Anda dapat melakukan ini dalam
ClickTheThing()
fungsinya sendiri, atau membuat fungsi lain dan memanggilClickTheThing()
tubuh if.Ini mirip dengan ide menggunakan bendera seperti yang disarankan dalam solusi lain, tetapi bendera dilindungi dari kode lain dan tidak dapat diubah di tempat lain karena sifat lokal ke fungsi.
sumber
Saya benar-benar menemukan dilema ini dengan Input Controller Xbox. Meskipun tidak persis sama, itu sangat mirip. Anda dapat mengubah kode dalam contoh saya sesuai dengan kebutuhan Anda.
Sunting: Situasi Anda akan menggunakan ini ->
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/ns-winuser-tagrawmouse
Dan Anda dapat mempelajari cara membuat kelas input mentah melalui ->
https://docs.microsoft.com/en-us/windows/desktop/inputdev/raw-input
Tapi .. sekarang ke algoritma super luar biasa ... tidak terlalu, tapi hei .. itu cukup keren :)
* Jadi ... kita dapat menyimpan status setiap tombol dan yang ditekan, Dirilis, dan Dimatikan !!! Kami juga dapat memeriksa Waktu Tunggu, tetapi itu memang membutuhkan pernyataan jika tunggal dan dapat memeriksa sejumlah tombol, tetapi ada beberapa aturan lihat di bawah untuk informasi ini.
Tentunya jika kita ingin memeriksa apakah ada sesuatu yang ditekan, dilepaskan, dll. Anda akan melakukan "Jika (Ini) {}", tetapi ini menunjukkan bagaimana kita bisa mendapatkan status pers dan kemudian mematikannya dari frame berikutnya sehingga Anda " ismousepressed "sebenarnya akan salah saat Anda memeriksa berikutnya.
Kode Lengkap Di Sini: https://github.com/JeremyDX/DX_B/blob/master/DX_B/XGameInput.cpp
Bagaimana itu bekerja..
Jadi saya tidak yakin nilai yang Anda terima ketika Anda menggambarkan apakah tombol ditekan atau tidak, tetapi pada dasarnya ketika saya memuat di XInput saya mendapatkan nilai 16bit antara 0 dan 65535 ini memiliki kemungkinan 15 bit untuk "Ditekan".
Masalahnya adalah setiap kali saya memeriksa ini, itu hanya akan memberi saya informasi terkini. Saya membutuhkan cara untuk mengubah kondisi saat ini menjadi Nilai Ditekan, Dirilis, dan Tahan.
Jadi yang saya lakukan adalah sebagai berikut.
Pertama-tama kita membuat variabel "CURRENT". Setiap kali kami memeriksa data ini, kami mengatur variabel "CURRENT" ke variabel "PREVIOUS" dan kemudian menyimpan data baru ke "Current" seperti yang terlihat di sini ->
Dengan informasi ini, inilah tempat menariknya !!
Kita sekarang dapat mencari tahu apakah Button sedang DI BAWAH!
Apa yang dilakukan pada dasarnya membandingkan dua nilai dan penekanan tombol apa saja yang ditampilkan di keduanya akan tetap 1 dan semua yang lain diatur ke 0.
Yaitu (1 | 2 | 4) & (2 | 4 | 8) akan menghasilkan (2 | 4).
Sekarang kita memiliki tombol "HELD" yang mana. Kita bisa mendapatkan sisanya.
Ditekan itu sederhana .. kami mengambil status "CURRENT" kami dan menghapus semua tombol yang ditekan.
Dirilis adalah sama, hanya kami membandingkannya dengan status TERAKHIR kami sebagai gantinya.
Jadi melihat situasi yang ditekan. Jika katakanlah Saat ini kami memiliki 2 | 4 | 8 ditekan. Kami menemukan bahwa 2 | 4 tempat diadakan. Ketika kita menghapus Bit Dimiliki kita hanya tersisa 8. Ini adalah bit yang baru ditekan untuk siklus ini.
Hal yang sama dapat diterapkan untuk Dirilis. Dalam skenario ini "LAST" disetel ke 1 | 2 | 4. Jadi saat kita menghapus 2 | 4 bit. Kita dibiarkan dengan 1. Jadi tombol 1 dirilis sejak frame terakhir.
Skenario di atas mungkin merupakan situasi paling ideal yang dapat Anda tawarkan untuk perbandingan bit dan memberikan 3 level data tanpa pernyataan if atau untuk loop hanya 3 perhitungan bit cepat.
Saya juga ingin mendokumentasikan data penahanan jadi meskipun situasi saya tidak sempurna ... apa yang dilakukannya adalah pada dasarnya kami menetapkan perangkat penampung yang ingin kami periksa.
Jadi setiap kali kita mengatur data Press / Release / Hold kami memeriksa apakah data hold masih sama dengan cek bit hold saat ini. Jika tidak kita atur kembali waktunya ke waktu sekarang. Dalam kasus saya, saya mengaturnya untuk membingkai indeks jadi saya tahu berapa banyak frame yang telah ditahan.
Kelemahan dari pendekatan ini adalah saya tidak bisa mendapatkan waktu penahanan individu, tetapi Anda dapat memeriksa beberapa bit sekaligus. Yaitu jika saya mengatur bit tahan ke 1 | 16 jika salah 1 atau 16 tidak ditahan ini akan gagal. Jadi itu mengharuskan SEMUA tombol-tombol itu ditekan untuk terus berdetak.
Kemudian jika Anda melihat dalam kode Anda akan melihat semua panggilan fungsi yang rapi.
Jadi contoh Anda akan dikurangi menjadi sekadar memeriksa apakah penekanan tombol terjadi dan penekanan tombol hanya dapat terjadi sekali dengan algoritma ini. Pada pemeriksaan berikutnya untuk menekannya tidak akan ada karena Anda tidak dapat menekan lebih dari satu kali Anda harus melepaskannya sebelum Anda dapat menekan lagi.
sumber
Jalan keluar tolol tapi Anda bisa menggunakan for for loop
sumber