Bab 5. User Input
“Jika terus ini, manusia akan berevolusi dan kehilangan semua jarinya, kecuali jari untuk menekan tombol.”
-Frank Lloyd Wright
TOPIK UTAMA
- Keyboard
- Mouse
- Joystick
- Abstraksi Hardware
- Tanggapan
- Pada penutupan
Sebuah model interaksi pengguna halus adalah kunci untuk setiap permainan yang baik.Tanpa mekanisme input pengguna yang memadai, gameplay menjadi terhambat dan frustrasi terjadi. Dalam bab ini, kita akan membahas dasar-dasar pengguna kontrol input. Sayangnya, akan ada beberapa aturan umum. Interaksi pengguna berlangsung pada relatif rendah tingkat abstraksi, dan implementasi cenderung sangat tergantung pada perangkat keras. Bila memungkinkan, umum teknik akan terkena. Tapi sebagian besar bab ni dikhususkan untuk metode input yang khusus yang tersedia untuk platform populer pada C, seperti API Win32 dan Microsoft DirectInput.
Keyboard
Keyboard adalah perangkat input utama untuk game berbasis PC, tetapi juga tersedia untuk ponsel, beberapa konsol, dan perangkat sawit. Yang membuat mereka, kemungkinan besar, perangkat input yang paling banyak tersedia. Sayangnya, seperti perangkat input populer sangat tidak cocok untuk game. Pemetaan keyboard mengambil waktu untuk belajar, dan gambaran umum tentang keyboard ini sama sekali tidak praktis untuk anak-anak kecil. Menjadi multifungsi perangkat yang dapat digunakan untuk mengetik dokumen dan untuk bermain game, tidak mengherankan bahwa keyboard dapat dibaca dengan menggunakan berbagai metode, tergantung pada kebutuhan spesifik dari aplikasi. Beberapa metode mengambil string penuh, yang lain bekerja atas dasar kunci-demi-kunci, dan sebagainya. Tapi untuk tujuan game, dua jenis rutinitas relevan. Pertama, ada rutinitas sinkron, yang menunggu sampai tombol ditekan dan kemudian melaporkannya ke aplikasi. Kedua, ada rutinitas asynchronous, yang segera kembali setelah dipanggil, dan memberikan informasi tentang aplikasi yang tombol ditekan, jika ada. Mode membaca sinkron digunakan untuk mengetik informasi, seperti nama karakter dalam role-playing game (RPG). Mereka bekerja dengan polling kontroler sampai pesan input kunci baru tiba.Tapi mereka tidak sangat baik cocok untuk gameplay nyata. Kode permainan harus terus menerus memeriksa untuk melihat apakah tombol ditekan, dan apapun respon, terus menggambar, melaksanakan AI, dan sebagainya. Jadi, pengendali asynchronous adalah cara untuk pergi. Mereka menyediakan tes cepat untuk memeriksa keadaan keyboard yang efisien.
Rutinitas Asynchronous juga bisa milik dua keluarga yang berbeda. Beberapa dari mereka yang dirancang untuk menguji keadaan kunci individu, sehingga pemrogram melewati kode kunci sebagai parameter dan mendapat negara sebagai terjadi. Lainnya, seperti yang terpapar oleh DirectInput, mengambil keyboard seluruh negara dalam satu panggilan, sehingga programmer kemudian dapat mengakses struktur data dan memeriksa keadaan setiap tombol tanpa lebih lanjut cek hardware. Tipe kedua rutin umumnya lebih efisien karena ada overhead kurang terlibat. Sebagai contoh, kita akan fokus pada panggilan asynchronous satu tombol untuk platform PC. Panggilan adalah Windows spesifik dan merupakan bagian dari Win32 API. Perintahnya adalah pendek GetAsyncKeyState (int keycode); Panggilan ini menerima kode kunci dan mengembalikan nilai pendek, yang mengkode informasi negara yang berbeda. Kuncinya kode kita lulus sebagai parameter dapat menjadi karakter dikapitalisasi, seperti “K”, atau kode kunci diperpanjang, yang digunakan untuk membaca karakter khusus. Dengan menggunakan kode kunci diperpanjang, kita bisa membaca kunci tertentu, seperti Hapus, tombol fungsi, Tabs, dan sebagainya. Tabel 5.1 menyediakan daftar kode kunci khusus utama untuk ini menelepon.
Tabel 5.1. Keycodes untuk panggilan GetAsyncKeyState
Keycode Deskripsi
VK_RSHIFT VK_SHIFT, VK, LSHIFT
|
Salah satu dari dua Shift
|
VK_MENU
|
Salah satu dari tombol Alt
|
VK_CTRL VK_RCTRL, VK_LCTRL
|
Setiap tombol Ctrl
|
VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT
|
Tombol kursor
|
VK_F1 … VK_F12
|
Tombol Fungsi
|
VK_ESCAPE
|
Esc
|
VK_SPACE
|
Spasi
|
VK_RETURN
|
Return
|
VK_HOME, VK_END, VK_PRIOR, VK_NEXT
|
keypad numerik
|
VK_BACK
|
Backspace
|
VK_TAB
|
Tab
|
VK_INSERT, VK_DELETE
|
Insert dan Delete
|
Nilai kembali mengkodekan keadaan kunci dilewatkan sebagai parameter. Bit yang paling signifikan diaktifkan jika tombol saat ditekan, sedangkan least significant bit diaktifkan jika kunci ini diaktifkan yang terakhir Waktu GetAsyncKeyState dipanggil. Berikut adalah contoh bagaimana untuk memeriksa apakah tombol Shift kiri adalah ditekan:
If (GetAsyncKeyState(VK_LSHIFT))
{
// whatever
}
Perhatikan bahwa, karena sifat panggilan, kita dapat memeriksa beberapa tombol.Contoh berikutnya menunjukkan bagaimana untuk menguji untuk Shift kiri DAN Kembali kombinasi:
If ((GetAsyncKeyState(VK_LSHIFT)) && (GetAsyncKeyState(VK_RETURN)))
{
// whatever
}
Seperti yang Anda lihat, setiap tes kunci memerlukan system call, yang dapat menyusahkan bagi sistem-sistem memeriksa banyak kunci yang berbeda. Sekarang, mari kita bandingkan panggilan ini dengan cek keyboard yang utuh, yang dapat dilakukan dengan menggunakan panggilan:
bool GetKeyboardState(PBYTE *lpKeyState);
Berikut hasilnya hanya mengkodekan apakah fungsi berhasil, dan daging yang sebenarnya dikembalikan sebagai array lulus sebagai referensi. Kemudian, pemeriksaan berturut-turut seperti berikut ini melakukan tes individu, yang tidak lain array lookup sederhana:
if (keystate[VK_RSHIFT])
{
// right shift was pressed
}
Sekali lagi, untuk game yang memeriksa banyak kunci (seperti simulator penerbangan), opsi ini bisa lebih baik daripada diulang panggilan ke GetAsyncKeyState. Programmer hanya perlu menyadari bahwa panggilan awal untuk GetKeyboardState diperlukan untuk memuat array. Lain yang mungkin perangkap untuk diperhatikan adalah bahwa modus kedua ini tidak segera memeriksa kunci ketika Anda melakukan tes. Tombol diperiksa pada panggilan untuk GetKeyboardState.Jika ada yang signifikan menunda antara tes ini dan lookup array, efek samping yang tidak diinginkan mungkin terjadi karena array akan mengandung “lama” nilai-nilai kunci. Keyboard dengan DirectInput DirectInput menyediakan akses asynchronous cepat untuk negara-negara kunci. Sebuah panggilan tunggal dapat mengambil keadaan. Seluruh Keyboard, tes berikutnya sehingga hanya tabel lookup. Operasi ini demikian sangat mirip dengan GetKeyboardState Win32 menelepon. Namun sebelum kita menggali ke dalam kode pembacaan Keyboard, kita perlu membahas bagaimana DirectInput bekerja. DirectInput merangkum keyboard, joystick, tikus, dan setiap masukan eksotis lainnya perifer bawah umum interface yang disebut perangkat. Operasi ini benar-benar mudah. Pertama-tama kita perlu boot DirectInput. Ini menyiratkan menciptakan objek DirectInput, dari mana semua benda lain yang berhubungan dengan proses input dapat berasal. Obyek DirectInput demikian dapat digunakan untuk membuat perangkat, yang merupakan antarmuka logis untuk peripheral. Setelah perangkat telah dibuat, kita perlu menentukan beberapa parameter, seperti format data yang kita inginkan untuk pertukaran dengan perangkat, dan tingkat koperasi, yang memberitahu DirectInput jika perangkat untuk dibagi di antara aplikasi yang berbeda atau jika kita perlu secara eksklusif.
Perangkat DirectInput kemudian dapat disurvei asynchronous. Kami query keadaan perangkat, tidak menunggu peristiwa tertentu seperti kunci atau tekan tombol. Ini berarti DirectInput akan mengambil snapshot dari kondisi saat ini perangkat dan mengembalikannya ke aplikasi sehingga dapat diproses. Sebagai ringkasan, berikut adalah daftar langkah-langkah terlibat dalam mendirikan keyboard DirectInput:
1. Membuat objek DirectInput.
2. Buat perangkat keyboard yang.
3. Mengatur format data untuk membacanya.
4. Mengatur tingkat koperasi akan Anda gunakan dengan sistem operasi.
5. Baca data yang diperlukan.
Mari kita sekarang beralih ke contoh spesifik, dimulai dengan kode DirectInput diperlukan untuk boot API. Itu kode dalam bagian ini telah diuji di kedua DirectX8 dan DirectX9. DirectInput hampir identik dalam kedua versi.
LPDIRECTINPUT8 g_pDI=NULL;
HRESULT hr=DirectInput8Create(GetModuleHandle(NULL),DIRECTINPUT_VERSION,
IID_IDirectInput8,(VOID**)&g_pDI,NULL)))
Dalam kode sebelumnya, parameter pertama digunakan untuk mengirim pegangan contoh untuk aplikasi yang menciptakan objek DirectInput. Kemudian, kita harus lulus versi DirectInput kita meminta.Itu makro DIRCTINPUT_VERSION adalah cara yang berguna untuk lulus nomor versi saat ini. Selanjutnya, kita harus lulus pengenal antarmuka yang unik untuk obyek yang kita minta. Kami menggunakan IID_IDirectInput8 untuk meminta DirectInput objek, tetapi kita bisa menggunakan parameter lain untuk menentukan versi ANSI atau Unicode antarmuka. Kami kemudian melewati pointer sehingga kami dapat menerima objek sudah diinisialisasi, dan parameter terakhir digunakan untuk melakukan Component Object Model (COM) agregasi. Anda mungkin tidak akan ingin agregat Anda Objek DirectInput untuk hal lain, sehingga meninggalkan ini sebagai NULL. Sekarang kita memiliki objek DirectInput siap untuk digunakan. Sekarang saatnya untuk kode keyboard nyata. Kami akan pertama meminta perangkat dan mengatur beberapa parameter yang menentukan bagaimana kita akan berkomunikasi dengannya. Kemudian, kita akan memeriksa kode sumber yang digunakan untuk membaca data dari keyboard.
Langkah pertama adalah untuk benar-benar meminta perangkat dari objek DirectInput.Hal ini dicapai dengan kalimat:
HRESULT hr =g_pDI->CreateDevice(GUID_SysKeyboard, &g_pKeyboard, NULL);
Panggilan harus menerima Global Unique Identifier (GUID) untuk perangkat yang diinginkan. DirectInput dibangun di atas dari COM, model pemrograman berorientasi obyek. Dalam COM, GUIDs digunakan untuk mengidentifikasi objek tertentu atau interface. Secara internal, GUID hanya struktur 128-bit, tetapi mereka digunakan untuk mewakili fungsi, benda, dan umumnya setiap DirectX membangun. Dalam kasus ini, GUIDs klasik untuk perangkat yang berbeda
- GUID_SysKeyboard: The default system keyboard.
- GUID_SysMouse: The default system mouse.
GUID tambahan dapat diberikan ke joystick. Namun, GUID ini tidak harus ditulis secara langsung, tetapi sebagai hasil dari panggilan untuk DirectInput8 :: EnumDevices. Kami akan mencakup joystick pada bagian berikutnya. Untuk keyboard kita, GUID_SYSKeyboard akan melakukan pekerjaan. Parameter kedua adalah pointer ke baru menciptakan perangkat, dan parameter terakhir ini lagi disediakan untuk agregasi dan sehingga harus diatur ke NULL. Sekarang, kita harus memberitahu keyboard bagaimana kita ingin bertukar data. Hal ini dicapai dengan panggilan untuk SetDataFormat, seperti yang ditunjukkan di sini:
HRESULT hr = g_pKeyboard->SetDataFormat( &c_dfDIKeyboard );
Panggilan harus menerima parameter tipe LPCDIDATAFORMAT, yang merupakan struktur idefinisikan sebagai:
typedef struct DIDATAFORMAT {
DWORD dwSize;
DWORD dwObjSize;
DWORD dwFlags;
DWORD dwDataSize;
DWORD dwNumObjs;
LPDIOBJECTDATAFORMAT rgodf;
} DIDATAFORMAT, *LPDIDATAFORMAT;
typedef const DIDATAFORMAT *LPCDIDATAFORMAT;
Struktur ini mengontrol jumlah objek kita akan meminta, format masing-masing, dan sebagainya. Karena struktur yang kompleks untuk mengisi, DirectInput sudah dilengkapi dengan beberapa format data yang telah ditetapkan yang dapat kita gunakan secara langsung. Untuk keyboard, c_dfDIKeyboard Format memberitahu DirectInput kita akan meminta keyboard lengkap, disimpan dalam array dari 256 byte. Selain itu, kita perlu memberitahu DirectInput tentang tingkat koperasi akan kita gunakan dengan perangkat ini. Ini dicapai dengan menggunakan garis:
HRESULT hr=g_pKeyboard->SetCooperativeLevel(hWnd, DISCL_FOREGROUND| DISCL_EXCLUSIVE);
Di sini kita melewati jendela menangani sebagai parameter pertama, dan parameter kedua adalah ATAU dari serangkaian bendera yang mengontrol tingkat koperasi. Dalam kasus ini, kita mengatakan DirectInput yang kita inginkan eksklusif mengakses dan bahwa akses ini seharusnya hanya berlaku jika aplikasi tersebut di latar depan. Sebagai aplikasi kita bergerak ke latar belakang, perangkat secara otomatis unacquired. Selain itu, kami perlu mendapatkan keyboard, sehingga kami dapat mulai query negaranya. Baris berikut akan melakukan bahwa bagi kita:
g_pKeyboard->Acquire();
Dan sekarang kami siap untuk mulai menggunakan keyboard. Berikut adalah potongan kode yang menyatakan kedua DirectInput dan keyboard, dan memastikan perangkat siap. Kesalahan pemeriksaan telah dihilangkan untuk kejelasan:
HRESULT hr;
hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, (VOID**)&g_pDI, NULL );
hr = g_pDI->CreateDevice( GUID_SysKeyboard, &g_pKeyboard, NULL );
hr = g_pKeyboard->SetDataFormat( &c_dfDIKeyboard );
hr = g_pKeyboard->SetCooperativeLevel( hDlg, dwCoopFlags );
hr = g_pKeyboard->Acquire();
Membaca keyboard bahkan lebih mudah daripada mempersiapkan itu. Yang harus kita lakukan adalah mempersiapkan array 256-byte dan menyebarkannya ke DirectInput dengan keyboard yang diperoleh untuk query negaranya:
BYTE diks[256]; // DirectInput keyboard state buffer
ZeroMemory( diks, sizeof(diks) );
hr = g_pKeyboard->GetDeviceState( sizeof(diks), diks );
Perhatikan bagaimana kita menghapus buffer dan kemudian menyebarkannya ke DirectInput. Seperti GetAsyncKeyState, kunci tertentu Kode harus digunakan setelah membaca query untuk setiap tombol. Dalam kasus ini, semua kunci yang diwakili oleh simbolik konstanta, seperti:
DIK_RETURN The return key
DIK_SPACE The space key
DIK_A … DIK_Z The alphabetic keys
DIK_F1 … DIK_F10 The function keys
Sekarang, untuk query kunci tertentu, kita harus menguji bit yang paling signifikan dari posisi array yang sesuai. Jika posisi yang diatur ke satu, kuncinya saat ini sedang ditekan. Dengan demikian, untuk memeriksa apakah tombol Kembali adalah diaktifkan, kode berikut dapat digunakan:
bool return_pressed=(buffer[DIK_RETURN] & 0x80)!=0);
Seperti biasa, kita bisa membaca kombinasi, sehingga kami dapat memeriksa apakah beberapa tombol yang ditekan secara bersamaan. Karena hanya ada satu DirectInput membaca di awal, ini hanya berbagai pencarian. Membaca keyboard benar-benar mudah. Tapi kita harus berhati-hati dengan mendapatkan dan unacquiring yang perangkat, yang dapat membuat kontroler masukan kami kerusakan. Kadang-kadang, terutama pada beberapa tingkat kooperatif mode, kita bisa kehilangan kontak dengan keyboard sesaat. Ini disebut unacquiring perangkat. Yang paling alasan populer untuk ini adalah bahwa aplikasi kita pindah ke latar belakang, sehingga kehilangan akses keyboard mendukung aplikasi lain, yang sekarang di latar depan. Beberapa acara lain mungkin membuat kita kehilangan jejak perangkat kami juga. Jika ini terjadi, kita akan menemukan dalam panggilan GetDeviceState berikutnya, yang akan gagal. Kami kemudian harus reacquire keyboard sehingga kami dapat terus query negaranya. Hal ini dicapai sebagai berikut:
BYTE diks[256]; // DirectInput keyboard state buffer
ZeroMemory( diks, sizeof(diks) );
hr = g_pKeyboard->GetDeviceState( sizeof(diks), diks );
if( FAILED(hr) )
{
hr = g_pKeyboard->Acquire();
while( hr == DIERR_INPUTLOST || hr== DIERR_OTHERAPPHASPRIO)
hr = g_pKeyboard->Acquire();
}
Perhatikan bagaimana kita mendeteksi kesalahan dan terus menelepon Memperoleh sampai kita memperoleh kembali akses ke perangkat. Setelah kami selesai dengan aplikasi kita, saatnya untuk melepaskan semua objek DirectInput damai. Melepaskan keyboard adalah proses dua langkah. Pertama, kita unacquire perangkat, kemudian lepaskan struktur datanya. Kedua, kita harus menghapus obyek DirectInput utama. Secara keseluruhan, urutan kehancuran dicapai dengan menggunakan kode berikut:
if( g_pKeyboard ) g_pKeyboard->Unacquire();
SAFE_RELEASE( g_pKeyboard );
SAFE_RELEASE( g_pDI );
Perhatikan bahwa kita menggunakan macro SAFE_RELEASE disediakan dengan DirectX untuk memastikan bahwa semua struktur data dan memori yang dialokasikan akan dihapus.
Mouse
Sejak awal mereka pada akhir 1960-an sebagai perangkat input CAD, mouse telah disesuaikan untuk banyak kegunaan termasuk game komputer. Mereka sangat populer di game PC, tapi game konsol biasanya tidak mendukung mereka. Tidak seperti keyboard atau joystick, mouse tidak hanya menghasilkan tombol atau tombol yang ditekan, tapi 2D
posisi juga. Ini menyediakan pilihan yang lebih banyak masukan pada biaya kurva belajar yang lebih tinggi untuk pemain. Mouse dapat digunakan dalam berbagai skenario, dari unit memetik dalam judul strategi real-time untuk populer mouselook ditemukan di sebagian besar penembak orang pertama. Dalam semua kasus, pengoperasian mouse dapat dibagi menjadi transmisi informasi posisional (berkat sensor mouse internal) dan mengirim tekan tombol dan pesan rilis. Mari kita periksa bagaimana mouse beroperasi di bawah DirectInput. Kode sumber ini sangat mirip dengan keyboard meminta karena DirectInput memperlakukan semua perangkat yang sama. Hal ini menguntungkan bagi programmer karena rincian paling dalam yang tersembunyi. Mari kita asumsikan kita memiliki objek DirectInput utama dan berjalan, dan mulai dengan penciptaan perangkat lulus:
LPDIRECTINPUTDEVICE g_pMouse;
HRESULT hr;
hr = g_pDI->CreateDevice(GUID_SysMouse, &g_pMouse, NULL);
Seperti yang Anda lihat, sangat mirip dengan meminta keyboard, satu-satunya perbedaan menjadi GUID kami lulus untuk meminta perangkat yang diinginkan. Kemudian, format data diatur sebagai berikut:
hr = g_pMouse->SetDataFormat(&c_dfDIMouse);
Dalam hal ini, parameter c_dfDIMouse memberitahu DirectInput kita akan melewati struktur
DIMOUSESTATE structure to
IDirectInputDevice::GetDeviceState.
Struktur ini memiliki tanda tangan berikut:
typedef struct DIMOUSESTATE {
LONG lX;
LONG lY;
LONG lZ;
BYTE rgbButtons[4];
} DIMOUSESTATE, *LPDIMOUSESTATE;
Struktur ini mengembalikan X dan posisi Y, dan Z sumbu opsional, yang biasanya ditugaskan untuk roda. Kemudian, tombol array bekerja seperti array Keyboard. Tombol ditekan jika bit high-order diatur. Avarian dari struktur ini adalah DIMOUSESTATE2, ditetapkan oleh c_dfDIMouse2 parameter. Satu-satunya perbedaan adalah bahwa yang terakhir mendukung delapan tombol bukannya empat didukung oleh DIMOUSESTATE. Ini terutama berguna dalam tikus tertentu yang digunakan untuk sistem CAD, misalnya. Setelah format data telah ditetapkan, kita perlu mengatur level kooperatif. Tidak ada kejutan di sini, karena kode tersebut persis sama dengan versi Keyboard:
hr=g_pMouse>SetCooperativeLevel(hWnd,DISCL_EXCLUSIVE|DISCL_FOREGROUND;
Selain itu, kita perlu untuk mendapatkan siap menggunakan perangkat dengan baris:
g_pMouse->Acquire();
Berikut adalah kode sumber penuh di review:
LPDIRECTINPUTDEVICE g_pMouse;
HRESULT hr = g_pDI->CreateDevice(GUID_SysMouse, &g_pMouse, NULL);
hr = g_pMouse->SetDataFormat(&c_dfDIMouse);
hr = g_pMouse->SetCooperativeLevel(hWnd,
DISCL_EXCLUSIVE | DISCL_FOREGROUND);
g_pMouse->Acquire();
Membaca dari mouse ini dicapai dengan panggilan GetDeviceState, yang akan mengembalikan LPDIMOUSESTATE struktur. Kode sumber akan:
DIMOUSESTATE dims; // DirectInput mouse state structure
ZeroMemory( &dims, sizeof(dims) );
hr = g_pMouse->GetDeviceState( sizeof(DIMOUSESTATE), &dims );
if( FAILED(hr) )
{
hr = g_pMouse->Acquire();
while( hr == DIERR_INPUTLOST || hr == DIERR_OTHERAPPHASPRIO ||
hr == DIERR_NOTACQUIRED)
hr = g_pMouse->Acquire();
}
Perhatikan bagaimana saya telah menambahkan kode pencegahan unacquiring untuk menghindari kehilangan jejak mouse kita karena kejadian tak terduga. Selain itu, kode ini sangat mirip dengan membaca keyboard.Untuk mengakses mouse atribut, yang harus kita lakukan adalah:
int MouseX = dims.lX;
int MouseY = dims.lY;
bool lbutton = (dims.rgbButtons[0] & 0x80)!=0);
Biasanya, tombol 0 ditugaskan untuk tombol kiri mouse, tombol 1 ditugaskan untuk orang yang tepat, dan tombol 2 adalah ditugaskan untuk tombol tengah (jika tersedia). Mengenai posisi, ingat bahwa mouse adalah relatif perangkat penunjuk. Ketika pertama kali diperoleh, posisi mouse adalah ulang ke (0,0).Kemudian, masing-masing membaca baru akan kembali perpindahan dari yang terakhir. Jadi, jika kita menggerakkan mouse vertikal, kita akan melihat perpindahan dalam Y arah. Tetapi ketika kita menghentikan gerakan, nilai mouse membaca akan kembali ke (0,0). Ingat, mouse tidak bekerja dengan posisi melainkan bekerja dengan perpindahan. Terakhir, tapi bukan yang terakhir, mouse biasanya dikonfigurasi poin X begitu negatif ke kiri, dan titik Y positif menjauh dari tubuh kita seperti kita duduk di meja. Selain itu, ingatlah untuk melepaskan mouse segera setelah Anda selesai menggunakannya. Kode ini lagi sangat mirip dengan kode rilis Keyboard:
if( g_pMouse ) g_pMouse->Unacquire();
SAFE_RELEASE( g_pMouse );
SAFE_RELEASE( g_pDI );
Mouselook
Sebuah penggunaan populer dari mouse adalah untuk menerapkan mouselook klasik yang digunakan dalam banyak penembak orang pertama. Itu mouselook mudah untuk kode setelah Anda memahami bagaimana mouse beroperasi.Yang harus kita lakukan adalah menggunakan kunci mengubah posisi kami, dan menggunakan mouse untuk reorientasi sudut pandang kita.Saya akan menjelaskan efek sepenuhnya, sehingga kita bisa menggabungkan apa yang telah kita pelajari tentang keyboard dan mouse. Permainan harus memiliki setidaknya empat derajat kebebasan. Kita harus memiliki posisi yang terdiri dari X dan Z nilai, dan kemudian yaw dan sudut lapangan. Nilai Y sering ditambahkan ke dalam campuran sehingga kita dapat mendaki berbeda ketinggian, tapi gulungan umumnya tidak diperlukan. Kami akan menggunakan pemetaan berikut:
- Mouse: Mouselook
- Panah kiri: memberondong kiri
- Panah kanan: memberondong tepat
- Panah: Maju
- Panah bawah: Mundur
Mari kita fokus pertama pada keyboard dan melupakan orientasi kedua. Dengan asumsi standar DirectInput Keyboard, kita akan membutuhkan kode berikut untuk menerapkan perilaku yang diinginkan:
int strafe= (buffer[DIK_RIGHT] & 0x80)!=0) – (buffer[DIK_LEFT] & 0x80)!=0);
int fwd= (buffer[DIK_UP] & 0x80)!=0) – (buffer[DIK_DOWN] & 0x80)!=0);
Perhatikan bagaimana kita telah elegan dikemas kontrol kursor. Dengan mengurangi arah yang berlawanan, kita mendapatkan dua nomor, memberondong dan fwd, dalam kisaran -1 ..1. Jumlah ini kemudian digunakan untuk menggerakkan posisi kami memperbarui rutin:
pos.x += fwd*FWDSPEED*elapsed*cos(yaw) +
strafe*STRAFESPEED*elapsed*cos(yaw+3.1416/2);
pos.z += fwd*FWDSPEED*elapsed*sin(yaw) +
strafe*STRAFESPEED*elapsed*sin(yaw+3.1416/2);
fwd dan strafe variabel mengontrol bagaimana masing-masing anggota dikalikan dengan -1-, 0, atau 1-untuk melakukan yang diinginkan efek. Sekarang, mari kita merawat lapangan dan yaw melalui mouse:
yaw+= YAWSPEED*elapsed*dims.lX;
pitch+= PITCHSPEED* elapsed*dims.lY;
Jadi sekarang kita memiliki pos.x baru kami, pos.z, yaw, dan struktur pemain lapangan diperbarui. Jelas, kita perlu menjaga dua perangkat hidup, dan kita perlu mendefinisikan semua konstanta dalam topi untuk mengatur kecepatan yang diinginkan. Perhatikan bagaimana setiap konstan dikalikan dengan faktor waktu berlalu, yang menyimpan waktu yang dibutuhkan untuk membuat frame terakhir. Dengan cara ini kita memastikan perangkat-independen kinerja. Untuk kelengkapan, di sini adalah source code yang diperlukan untuk menghitung semua parameter kamera spesifik siap untuk dipasang ke OpenGL atau DirectX pipa:
point campos(pos.x,pos.y,pos.z);
point camlookat(pos.x+cos(yaw)*cos(pitch),pos.y+sin(pitch),
pos.z+sin(yaw)*cos(pitch));
lookat perhitungan koordinat hanya pemetaan bola menggunakan pitch dan yaw.Tergantung pada bagaimana kapak Anda ditata, tanda mungkin berubah atau Anda mungkin perlu menambahkan sudut konstan seperti Pi ke lapangan nilai-nilai.
Joystick
Joystick ini diperkenalkan pada 1970-an sebagai cara untuk mewakili data posisi dengan mudah. Model pertama adalah dibatasi untuk tes biner: joystick itu kembali 0 atau 1 untuk mewakili apakah itu sedang diaktifkan atau tidak. Dengan demikian, sebagian besar joystick diperbolehkan sembilan nilai posisi: satu untuk joystick yang berpusat dan delapan untuk N, S, E, W, SW, SE, NW, dan NE. Biasanya, posisi joystick yang dipetakan ke nilai integer, dengan sedikit tambahan digunakan untuk mewakili tekan tombol. Kebanyakan joystick dari komputer delapan-bit seperti ini. Sebagai rumah pengembangan perangkat lunak menciptakan simulasi yang lebih baik, joystick mulai membaik juga. Kontinyu-output joystick muncul untuk memenuhi komunitas simulasi penerbangan, tetapi karena mereka menawarkan kontrol yang lebih baik, mereka menjadi mainstream. Hari ini, semua joystick memetakan kecenderungan tongkat ‘untuk terus menerus rentang nilai, sehingga kita dapat mengontrol karakter kita tepat. Mengontrol joystick sedikit lebih kompleks daripada bekerja dengan keyboard atau mouse. Joystick datang berbagai bentuk dan konfigurasi, sehingga proses pengambilan deteksi dan data sedikit lebih terlibat. Beberapa gamepads memiliki dua controller, sedangkan yang lain memiliki tongkat dan point-of-view (POV). Selain itu, jumlah tombol bervariasi dari model ke model, membuat proses mendeteksi joystick trivial. Di DirectInput, kita harus bertanya API untuk menghitung perangkat sehingga autodetects joystick sehingga kita dapat menggunakan itu. Untuk contoh ini, saya akan menganggap kita sudah memiliki objek DirectInput siap.Langkah pertama adalah meminta DirectInput untuk menghitung setiap joystick itu mendeteksi. Hal ini dicapai dengan menggunakan panggilan:
HRESULT hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL,
EnumJoysticksCallback,
NULL, DIEDFL_ATTACHEDONLY ) ) )
Parameter pertama memberitahu DirectInput yang jenis perangkat kita ingin mendeteksi. Ini bisa menjadi mouse (DI8DEVCLASS_POINTER) atau keyboard (DI8DEVCLASS_KEYBOARD). Dalam hal ini, kami meminta permainan controller, yang berlaku untuk kedua gamepads dan joystick dari semua jenis. Sekarang sampai pada bagian rumit:
DirectInput mendeteksi semua perangkat kandidat. Bayangkan bahwa kita memiliki dua joystick (misalnya, dalam dua pemain permainan). DirectInput akan perlu kembali dua perangkat. Jadi, bukannya melakukan sihir parameter memungkinkan ini, DirectInput bekerja dengan callback. Callback adalah fungsi user-defined yang dipanggil dari
dalam pelaksanaan system call. Dalam kasus ini, kami menyediakan EnumJoysticksCallback tersebut, fungsi kita menulis yang akan mendapatkan dipicu sekali untuk setiap terdeteksi joystick. Internal dari fungsi yang harus mengambil GUID, mengalokasikan objek perangkat, dan sebagainya. Ini adalah sedikit lebih rumit daripada kembali daftar pointer, tapi di sisi lain, memungkinkan fleksibilitas yang lebih besar. Kami akan memeriksa callback kami dalam satu detik. Mari kita pertama menyelesaikan profil panggilan dengan menyatakan bahwa parameter ketiga adalah parameter pengguna didefinisikan sebagai dilewatkan ke callback (biasanya NULL), sedangkan parameter terakhir adalah bendera pencacahan.
DIEDFL_ATTACHEDONLY digunakan untuk menyatakan bahwa kita hanya ingin mendeteksi alat-alat yang terpasang dengan benar dan diinstal, cara DIEDFL_FORCEFEEDBACK sama digunakan untuk membatasi pencacahan untuk memaksa umpan balik joystick. Berikut adalah source code untuk fungsi numJoysticksCallback:
BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext )
{
HRESULT hr;
hr = g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pJoystick, NULL );
if( FAILED(hr) ) return DIENUM_CONTINUE;
return DIENUM_STOP;
}
Perhatikan bagaimana callback menerima contoh perangkat, jadi kami hanya harus membuat perangkat dipake misalnya. Ini adalah contoh yang relatif sederhana, di mana kita kembali ke aplikasi segera setelah kami telah menemukan satu joystick. Jika pengguna memiliki dua joystick yang terpasang ke komputer, kode ini hanya akan menghitung pertama. Varian dapat digunakan untuk menyimpan setiap joystick dalam linked list, sehingga pengguna dapat kemudian pilih joystick dia benar-benar ingin Anda gunakan dari daftar drop-down.
Setelah joystick telah disebutkan, kita dapat mengatur format data dan tingkat koperasi.Tidak ada berita herejust pengulangan dari kode yang diperlukan untuk keyboard dan mouse:
HRESULT hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick );
HRESULT hr = g_pJoystick->SetCooperativeLevel( hWnd, DISCL_EXCLUSIVE |
DISCL_FOREGROUND );
Ekstra potongan kode harus digunakan untuk mengatur rentang output untuk joystick.Karena perangkat dengan sumbu analog, apa yang akan menjadi rentang nilai output? Apakah akan -1 .. 1 atau -1000 .. 1000? Kita perlu membuat yakin perilaku joystick diinisialisasi dengan benar. Dalam kasus kami, kami akan membuat joystick merespon dengan nilai dari -100 .. 100, seperti persentase. Untuk melakukannya, kita perlu menggunakan callback kedua. Tetapi, pertama kita membutuhkan panggilan berikut, yang meminta benda-benda yang berhubungan dengan joystick:
g_pJoystick->EnumObjects(EnumObjectsCallback, (VOID*)hWnd, DIDFT_ALL);
Obyek dapat kapak, tombol, POVs, dan sebagainya. Lalu, panggilan akan merespon melalui callback disediakan. Di sini adalah kode sumber untuk itu callback, yang melakukan inisialisasi sumbu:
BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
VOID* pContext )
{
HWND hDlg = (HWND)pContext;
if( pdidoi->dwType & DIDFT_AXIS )
{
DIPROPRANGE diprg;
diprg.diph.dwSize = sizeof(DIPROPRANGE);
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
diprg.diph.dwHow = DIPH_BYID;
diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis
diprg.lMin = -100;
diprg.lMax = +100;
if( FAILED( g_pJoystick->SetProperty( DIPROP_RANGE, &diprg.diph ) ) )
return DIENUM_STOP;
}
}
Seperti dengan callback sebelumnya, rutin ini dipanggil sekali untuk setiap objek.Kemudian, jika pemeriksaan kalimat apakah objek dikembalikan sebenarnya sumbu, dan jika demikian, menggunakan panggilan setProperty untuk menentukan respon jangkauan. The setProperty panggilan dapat digunakan untuk fungsi yang lebih eksotis, seperti kalibrasi joystick. Itu
Parameter pertama mendukung banyak konstanta simbolis lainnya yang dapat digunakan untuk tujuan ini. Untungnya, membaca dari joystick tidak serumit memulainya. Berikut kode sumber tidak banyak berbeda dari keyboard atau mouse. Satu-satunya perbedaan adalah bahwa kita perlu memanggil Poll () sebelum benar-benar membaca dari joystick. Joystick yang disurvei perangkat, yang berarti mereka tidak menghasilkan interrupt, sehingga perlu
melakukan jajak pendapat sebelum mengambil negara mereka. Selain itu, kode sangat mudah:
hr = g_pJoystick->Poll();
if( FAILED(hr) )
{
hr = g_pJoystick->Acquire();
while( hr == DIERR_INPUTLOST || hr== DIERR_OTHERAPPHASPRIO)
hr = g_pJoystick->Acquire();
return S_OK;
}
DIJOYSTATE js;
hr = g_pJoystick->GetDeviceState( sizeof(DIJOYSTATE), &js ));
Kode ini mengembalikan struktur DIJOYSTATE dengan semua joystick informasi negara. Profil panggilan adalah sebagai berikut:
typedef struct DIJOYSTATE {
LONG lX;
LONG lY;
LONG lZ;
LONG lRx;
LONG lRy;
LONG lRz;
LONG rglSlider[2];
DWORD rgdwPOV[4];
BYTE rgbButtons[32];
} DIJOYSTATE, *LPDIJOYSTATE;
Struktur ini harus cukup untuk sebagian besar menggunakan. Namun, ada struktur yang lebih terlibat dengan banyak ekstra parameter yang tersedia di bawah nama DIJOYSTATE2. Yang harus Anda lakukan adalah mengubah panggilan SetDataFormat sesuai:
HRESULT hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick2 );
Respon Curves
Kita telah melihat bagaimana joystick analog memetakan posisi controller untuk berbagai berkesinambungan nilai sehingga kita bisa mendeteksi variasi halus. Perilaku ini dapat menjadi sekutu terbaik kami atau mimpi buruk tergantung bagaimana kita
menanganinya. Sebagai contoh, bayangkan sebuah permainan seperti Mario, di mana tidak ada kontrol kecepatan: Mario hanya berjalan kiri, berjalan tepat, atau berdiri diam. Jadi bagaimana kita menerapkan bahwa menggunakan controller analog? Kita harus
discretize rentang output jadi kami hanya mendapatkan tiga nilai yang mungkin:
-1 Untuk menjalankan kiri
0 untuk berdiri diam
1 untuk menjalankan hak
Dengan asumsi bahwa output analog dalam rentang -100 .. 100, kita perlu mendefinisikan fungsi transfer yang memetakan nomor dalam kisaran -100 .. 100 ke nomor dalam kisaran -1 .. 1. Memilih pengalihan fungsi-sering disebut respon kurva-akurat adalah kunci untuk menjaga komunikasi yang baik dengan pemain. Bayangkan bahwa kita melakukan sesuatu seperti ini:
-1 Untuk nilai [-100 .. -1]
0 untuk nilai 0
1 untuk nilai-nilai [1 .. 100]
Ini berarti sedikit variasi atau Decentering dari controller akan memicu gerakan, membuat
unplayable permainan. Untuk jenis-jenis kurva respon (yang mengubah rentang analog ke satu diskrit), kita harus menyediakan cukup rentang dinamis sehingga setiap nilai akan dipetakan dengan benar. Sebagai contoh, banyak solusi yang lebih baik akan
-1 Untuk nilai [-100 .. -25] untuk nilai [-24 .. 24] 1 untuk nilai-nilai [25 .. 100]
Decalibrations cara ini minimal tidak akan memicu joystick oleh kesalahan. Kurva ini respon dapat dilihat pada Gambar 5.1.
Gambar 5.1. Kurva respons tanpa (kiri) dan dengan (kanan) zona mati.
Kebanyakan game menggunakan kontrol analog hari ini. Pengendali analog mengembalikan nilai dalam kisaran yang kontinyu, dan kita menggunakan nilai tersebut untuk melaksanakan berbagai tingkat pengaruh (baik itu kecepatan, berputar, dll) dalam dunia permainan kami. Sebuah pesawat akan menyelam lebih cepat jika kita tarik controller sepenuhnya, mobil akan mempercepat lebih agresif, dan sebagainya. Namun, kontrol analog juga membutuhkan kurva respon. Tanpa kurva respons, mobil akan tetap balik jika controller hanya minimal decalibrated. Meskipun ini adalah kemungkinan besar masalah controller, kami kode harus berurusan dengan itu sehingga pemain menikmati pengalaman bermain mulus. Seperti yang Anda duga, kita mulai dengan menetralkan zona sekitar pusat controller untuk mencegah gerakan karena kalibrasi buruk. Tapi akan netralisasi peta kecepatan linear ke controller posisi (seperti pada Gambar 5.2, kiri), atau akan menggunakan kurva (seperti yang di sebelah kanan pada Gambar 5.2)? Menggunakan kurva seperti parabola akan berguna untuk menerapkan efek inersia, di mana pemain perlu mengontrol rasa kecenderungan hati-hati. Itu membuat permainan sedikit lebih keras untuk menguasai karena kesalahan kecil dalam kontrol menghasilkan variasi besar dalam efek.
Gambar 5.2. Jenis kurva respon.
Abstraksi Hardware
Game yang berjalan pada platform yang mendukung berbagai kontroler masukan menawarkan pengalaman gaming terkaya. Ini adalah kasus tidak hanya dengan PC (yang menggunakan keyboard, mouse, dan joystick), tetapi juga dengan sebagian besar
konsol game melalui perangkat eksotis. Dalam beberapa tahun terakhir, kami telah menggunakan standar controller, pesawat-gaya joystick, snowboards, bantalan tarian segala macam, dan bahkan pancing! Ada dua jalur pengembang mungkin mengikuti ketika coding untuk platform tersebut.Beberapa game akan memilih untuk menggunakan satu (dan hanya satu) dari pengendali yang ada. Hal ini terjadi di sebagian besar game strategi, yang untuk alasan gameplay biasanya dikodekan dengan mouse PC dalam pikiran. Di sisi lain, beberapa permainan lain akan membiarkan pengguna memilih metode input yang dia inginkan untuk mempekerjakan.Aksi game untuk PC sering dapat bermain dengan keyboard, joystick, gamepad, dan bahkan kadang-kadang mouse.Meskipun baik untuk pemain, itu datang pada biaya rumit kode penanganan masukan signifikan, dan ini adalah persis di mana hardware abstraksi menjadi masalah. Dengan abstraksi perangkat keras, maksud saya coding permainan dengan “virtual” controller dalam pikiran, sehingga setiap controller yang sesuai dengan profil abstrak dapat dipasang ke dalam kode dengan nol dampak pada mesin permainan. Semua Anda perlu Anda lakukan adalah menulis kontroler handler generik (biasanya, sebuah kelas abstrak murni) yang spesifik pengendali berasal melalui warisan. Kemudian, pada saat runtime, hanya jenis kontroler dipilih oleh pemain adalah dibuat, menyediakan cara halus dan elegan mengintegrasikan kontroler yang berbeda.Gambar 5.3 menunjukkan bagaimana Struktur kelas ini akan bekerja.
Gambar 5.3. Sebuah struktur kelas mungkin untuk mengimplementasikan perangkat abstraksi.
Perhatikan bagaimana, demi kejelasan, saya telah membuat sebuah kontroler global yang sangat menyerupai sebuah game konsol. Ini memiliki dua kontroler arah dan empat tombol. Input ini kemudian dapat dipetakan ke keyboard (Menggunakan tombol untuk pilihan yang berbeda), sebuah joystick analog, atau joystick digital. Satu-satunya barang kita perlu diwaspadai adalah kepekaan yang berbeda ditemukan di tombol dan tongkat. Sebuah kunci dapat ditekan atau dilepaskan, sehingga hanya membutuhkan nilai biner, sedangkan joystick mungkin akan memetakan ke satu set terus menerus. Dengan demikian, beberapa
kode diskritisasi akan diminta untuk menawarkan output standar. Sebuah alternatif yang lebih baik jika Anda menggunakan DirectInput adalah untuk mengambil keuntungan dari pemetaan tindakan, yang memungkinkan perangkat Anda untuk kembali tidak nilai-nilai negara tertentu tetapi berorientasi pada nilai permainan. Kita dapat menetapkan aktivitas perangkat untuk acara permainan, sehingga controller masukan akan menerima nilai-nilai yang entah bagaimana perangkat independen. Untuk Misalnya, kita dapat memiliki joystick dan menetapkan gerakan pada sumbu X ke “muka kiri” tindakan. Kita bisa kemudian menetapkan tombol khusus untuk acara yang sama, sehingga kode kontrol input menjadi independen perangkat. Kita dapat mengubah perangkat, dan sebagai kontrol yang menerima pesan abstrak, semuanya akan tetap bekerja. Pemetaan tindakan tidak sulit untuk mendapatkan bekerja di bawah DirectInput, meskipun kode terlalu panjang untuk menjadi rinci di sini. Langkah pertama adalah menentukan tindakan dalam permainan kami:
enum GAME_ACTIONS {
WALK,
WALK_LEFT,
WALK_RIGHT,
JUMP
QUIT
};
Kemudian, kita harus menyediakan peta tindakan yang berkaitan sumbu dan tombol atau kunci untuk tindakan yang berbeda:
DIACTION g_adiaActionMap[] =
{
// Joystick input mappings
{ WALK, DIAXIS_FIGHTINGH_LATERAL, 0, ACTION_NAMES[WALK], },
{ JUMP, DIBUTTON_FIGHTINGH_JUMP, 0, ACTION_NAMES[JUMP], },
// Keyboard input mappings
{ WALK_LEFT, DIKEYBOARD_LEFT, 0, ACTION_NAMES[WALK_LEFT], },
{ WALK_RIGHT, DIKEYBOARD_RIGHT, 0, ACTION_NAMES[WALK_RIGHT], },
{ JUMP, DIKEYBOARD_J, 0, ACTION_NAMES[JUMP], },
{ QUIT, DIKEYBOARD_Q, DIA_APPFIXED, ACTION_NAMES[QUIT], },
// Mouse input mappings
{ WALK, DIMOUSE_XAXIS, 0, ACTION_NAMES[WALK], },
{ JUMP, DIMOUSE_BUTTON0, 0, ACTION_NAMES[JUMP], },
};
Inisialisasi sistem input akan sangat mirip dengan contoh sebelumnya. Semua kita perlu
lakukan adalah menghitung semua perangkat input didukung oleh peta tindakan. Hal ini akan dicapai dengan panggilan untuk EnumerateDevicesBySemantics, yang akan menerima peta tindakan dan membutuhkan callback di mana semua perangkat mungkin akan dikembalikan. Callback kemudian harus membangun peta tindakan untuk itu spesifik
perangkat. Setelah itu dibersihkan, panggilan membaca berikutnya akan kembali perangkat-data spesifik, seperti berikut contoh:
hr = pdidDevice->Poll();
hr = pdidDevice->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
rgdod, &dwItems, 0 );
for( DWORD j=0; j<dwItems; j++ )
{
UINT_PTR dwAction = rgdod[j].uAppData;
switch( dwAction )
{
case WALK:
(…)
}
}
Perhatikan bahwa GetDeviceData mengembalikan daftar tindakan, sehingga kami dapat memproses beberapa tombol atau kunci dalam yang sama lingkaran.
Tanggapan
Banyak pengendali masukan datang dengan semacam umpan balik kekuatan hari ini, dari gamepads gemuruh pada permainan konsol untuk joystick umpan balik kekuatan penuh. Teknik ini hanyalah lapisan lain dalam pengejaran kita perendaman
dan realisme. Hardware umpan balik kekuatan mensimulasikan getaran dan efek berbasis kekuatan lain dengan memasukkan satu atau lebih motor yang dapat dikonfigurasi melalui pemrograman. Motor ini bervariasi posisi atau perlawanan dari permainan controller, dan dengan pemrograman urutan gerakan cepat, ilusi getaran dan kekuatan yang dicapai. Jumlah motor dan jenis kontroler menentukan kualitas efek. Pada ujung bawah spektrum, semua gamepads-sejak awal PlayStation controller dukungan semacam umpan balik kekuatan, jenis biasanya berbeda getaran seperti ponsel. Joystick dapat melakukan jauh lebih canggih rutinitas. Sebagai contoh, adalah mungkin untuk program joystick untuk menggambar lingkaran dengan tongkat, serta banyak efek menarik lainnya. Perangkat umpan balik kekuatan Pemrograman melibatkan tiga langkah. Pertama, kita perlu cara untuk membuat atau menggambarkan efek yang diinginkan dalam hal bagaimana hal itu mempengaruhi posisi controller, kekuatan, dan sebagainya. Beberapa alat yang tersedia untuk tujuan ini. Kedua, efeknya harus dimuat ke API masukan. Ketiga, kita perlu cara untuk mereproduksi efek pada runtime, baik secara manual memicu atau dengan menugaskan ke acara tertentu seperti pewaktu atau tekan tombol tertentu. Sayangnya, tiga langkah sebagian besar tergantung platform, karena pengembangan aplikasi yang mendukung umpan balik kekuatan sangat tergantung pada API. Untuk sisa bagian ini, kita akan fokus pada Microsoft solusi DirectInput untuk memberikan contoh bagaimana perangkat ini bekerja. DirectInput memungkinkan penciptaan efek umpan balik kekuatan baik dari alat-konten penciptaan eksternal atau oleh menggunakan satu set khusus perintah dari dalam aplikasi kita. Cara termudah adalah dengan menggunakan alat penciptaan, disebut Angkatan Editor (lihat Gambar 5.4). Editor memiliki tampilan dan nuansa mirip dengan aplikasi audio, tetapi bekerja dengan kurva kekuatan bukan suara. Kita dapat memilih berbagai jenis bentuk gelombang (persegi, gigi gergaji, sinus, dan sebagainya) dan memodulasi mereka, sehingga efek yang dihasilkan dapat dipercaya. Kita bahkan dapat prototipe efek dari editor, mendapatkan preview tentang bagaimana produk akhir akan merasa. Ketika kami senang dengan hasilnya, kami dapat menyimpannya ke file. FFE, yang kemudian akan dibaca oleh DirectInput.
Gambar 5.4. Microsoft Angkatan Editor, dibangun ke dalam DirectX SDK.
Dari sudut aplikasi pandang, perbedaan mulai dari tahap pencacahan perangkat. Kita perlu query untuk perangkat gaya umpan balik yang kompatibel saja, sebagai berikut:
HRESULT hr = g_pDI->EnumDevices( 0, EnumFFDevicesCallback, 0,
DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK );
Tidak ada perubahan pada fungsi EnumDevicesCallback diperlukan. Sebuah joystick gaya umpan balik adalah, sejauh perangkat pergi, hanya joystick biasa yang dapat dibaca seperti biasa. Langkah selanjutnya adalah membaca yang relevan efek dari file FFE.. Satu berkas tersebut dapat berisi beberapa efek, jadi kita akan menggunakan panggilan Enum dengan Callback sebuah berfungsi untuk mengambil efek tersebut. Berikut adalah panggilan Enum:
HRESULT hr = g_pFFDevice->EnumEffectsInFile( strFileName,
EnumAndCreateEffectsCallback,
NULL, DIFEF_MODIFYIFNEEDED );
Mari kita sekarang memeriksa kode untuk callback. Untuk mempermudah, saya akan menganggap file FFE. Hanya berisi satu Efek, maka setelah mengambilnya, tidak ada panggilan lebih lanjut akan ditempuh.Berikut ini adalah implementasi yang disarankan dari
rutin, yang menyimpan efek dalam struktur gigih untuk akses nanti. Seperti biasa, pengecekan error dihapus untuk kejelasan:
LPDIRECTINPUTEFFECT pDIEffect;
BOOL CALLBACK EnumAndCreateEffectsCallback( LPCDIFILEEFFECT pDIFileEffect, VOID* pvRef )
{
HRESULT hr = g_pFFDevice->CreateEffect( pDIFileEffect->GuidEffect,
pDIFileEffect->lpDiEffect,
&pDIEffect, NULL );
return DIENUM_STOP;
}
Kemudian, yang perlu kita lakukan adalah memicu efek yang diperlukan. Ini adalah proses dua langkah. Pertama, kita perlu berhenti setiap angkatan sebelumnya, sehingga kedua efek tidak bertentangan. Kedua, kita memicu efek baru. Berikut ini adalah sederhana contoh-pertama, berhenti untuk efek lainnya:
HRESULT hr = g_pFFDevice->SendForceFeedbackCommand( DISFFC_STOPALL );
kedua, pelaksanaan efek:
HRESULT hr = pDIEffect->Start(1, 0);
Parameter pertama adalah jumlah pengulangan yang kita inginkan untuk efek, dan parameter kedua adalah bendera parameter. Melewati INFINITE sebagai parameter pertama menyebabkan efek untuk loop selamanya, seperti mesin getaran untuk helikopter. Ini jenis efek harus dihentikan secara manual dengan menelpon ke:
pDIEffect->Stop();
Kekuatan API umpan balik dari DirectInput sangat komprehensif. Tapi kita hanya menggores permukaan. Sebagai contoh, kita dapat mencampur efek dinamis atau bahkan membuat mereka prosedural tanpa menggunakan Force Editor. Dengan waktu dan banyak eksperimen, banyak menggunakan menarik dapat dirancang.
Penutup
Input adalah subjek yang luas, membutuhkan banyak teknik. Tapi sayangnya, tidak ada standar yang luas yang tersedia. Meskipun kami telah berfokus pada teknik-teknik khusus untuk PC, sebagian besar ide-ide yang berlaku untuk platform lain baik. Ide seperti abstraksi perangkat, mouselooks, atau kurva respon bersifat universal dan juga digunakan pada platform lainnya.
http://www.tar.hu/gamealgorithms/
http://www.tar.hu/gamealgorithms/ch05.html
http://www.tar.hu/gamealgorithms/ch05lev1sec1.html
http://www.tar.hu/gamealgorithms/ch05lev1sec2.html
http://www.tar.hu/gamealgorithms/ch05lev1sec3.html
http://www.tar.hu/gamealgorithms/ch05lev1sec4.html
http://www.tar.hu/gamealgorithms/ch05lev1sec5.html
http://www.tar.hu/gamealgorithms/ch05lev1sec6.html