Bagikan melalui


Gamepad dan getaran

Halaman ini menjelaskan dasar-dasar pemrograman untuk pad game menggunakan [Windows.Gaming.Input.Gamepad][game pad] dan API terkait untuk Platform Windows Universal (UWP).

Dengan membaca halaman ini, Anda akan mempelajari:

  • cara mengumpulkan daftar pad permainan yang terhubung dan penggunanya
  • cara mendeteksi bahwa pad permainan telah ditambahkan atau dihapus
  • cara membaca input dari satu atau beberapa pad game
  • cara mengirim perintah getaran dan impuls
  • bagaimana pad game bereaksi sebagai perangkat navigasi UI

Gambaran umum Gamepad

Gamepad seperti Xbox Wireless Controller dan Xbox Wireless Controller S adalah perangkat input game tujuan umum. Mereka adalah perangkat input standar di Xbox One dan pilihan umum untuk gamer Windows ketika mereka tidak mendukung keyboard dan mouse. Gamepad didukung di aplikasi Windows 10 atau Windows 11 dan Xbox UWP melalui namespace Windows.Gaming.Input .

Gamepad Xbox One dilengkapi dengan tombol arah (atau D-pad); Tombol A, B, X, Y, Tampilan, dan Menu ; thumbstick kiri dan kanan, bumper, dan pemicu; dan total empat motor getaran. Kedua thumbstick menyediakan pembacaan analog ganda di sumbu X dan Y, dan juga bertindak sebagai tombol saat ditekan ke dalam. Setiap pemicu menyediakan pembacaan analog yang mewakili seberapa jauh pemicu ditarik kembali.

Catatan

Windows.Gaming.Input.Gamepad juga mendukung gamepad Xbox 360, yang memiliki tata letak kontrol yang sama dengan gamepad Xbox One standar.

Pemicu getaran dan impuls

Gamepad Xbox One menyediakan dua motor independen untuk getaran gamepad yang kuat dan halus serta dua motor khusus untuk memberikan getaran tajam ke setiap pemicu (fitur unik ini adalah alasan pemicu gamepad Xbox One disebut sebagai pemicu impuls).

Catatan

Gamepad Xbox 360 tidak dilengkapi dengan pemicu impuls.

Untuk informasi selengkapnya, lihat Gambaran umum pemicu getaran dan impuls.

Deadzone thumbstick

Thumbstick saat istirahat di posisi tengah idealnya akan menghasilkan pembacaan netral yang sama di sumbu X dan Y setiap saat. Namun, karena kekuatan mekanis dan sensitivitas thumbstick, pembacaan aktual di posisi tengah hanya perkiraan nilai netral yang ideal dan dapat bervariasi antara pembacaan berikutnya. Untuk alasan ini, Anda harus selalu menggunakan deadzone kecil—rentang nilai di dekat posisi pusat ideal yang diabaikan—untuk mengimbangi perbedaan manufaktur, keausan mekanis, atau masalah gamepad lainnya.

Deadzone yang lebih besar menawarkan strategi sederhana untuk memisahkan input yang disengaja dari input yang tidak disengaja.

Untuk informasi selengkapnya, lihat Membaca thumbstick.

Navigasi antarmuka pengguna

Untuk meringankan beban mendukung perangkat input yang berbeda untuk navigasi antarmuka pengguna dan untuk mendorong konsistensi antara game dan perangkat, sebagian besar perangkat input fisik secara bersamaan bertindak sebagai perangkat input logis terpisah yang disebut pengontrol navigasi UI. Pengontrol navigasi UI menyediakan kosakata umum untuk perintah navigasi UI di seluruh perangkat input.

Sebagai pengontrol navigasi UI, gamepad memetakan set perintah navigasi yang diperlukan ke thumbstick kiri, tombol D-pad, Tampilan, Menu, A, dan B .

Perintah navigasi Input gamepad
Naik Jempol kiri ke atas/D-pad ke atas
Tidak berfungsi Thumbstick kiri ke bawah/ D-pad ke bawah
Left Thumbstick kiri/ D-pad kiri
Right Thumbstick kiri kanan/ D-pad kanan
Tampilkan Tombol Tampilan
Menu Tombol Menu
Terima Tombol
Batalkan Tombol B

Selain itu, gamepad memetakan semua kumpulan perintah navigasi opsional ke input yang tersisa.

Perintah navigasi Input gamepad
Halaman Atas Pemicu kiri
Halaman Bawah Pemicu yang tepat
Halaman Kiri Bumper kiri
Halaman Kanan Bumper kanan
Gulir Ke Atas Jempol kanan ke atas
Gulir ke Bawah Thumbstick kanan ke bawah
Gulir Ke Kiri Thumbstick kanan kiri
Gulir Ke Kanan Thumbstick kanan
Konteks 1 Tombol X
Konteks 2 Tombol Y
Konteks 3 Tekan thumbstick kiri
Konteks 4 Tekan thumbstick kanan

Mendeteksi dan melacak gamepad

Gamepad dikelola oleh sistem, oleh karena itu Anda tidak perlu membuat atau menginisialisasinya. Sistem ini menyediakan daftar gamepad dan peristiwa yang terhubung untuk memberi tahu Anda ketika gamepad ditambahkan atau dihapus.

Daftar gamepad

Kelas Gamepad menyediakan properti statis, Gamepads, yang merupakan daftar gamepad baca-saja yang saat ini terhubung. Karena Anda mungkin hanya tertarik pada beberapa gamepad yang terhubung, disarankan agar Anda mempertahankan koleksi Anda sendiri alih-alih mengaksesnya melalui Gamepads properti .

Contoh berikut menyalin semua gamepad yang terhubung ke dalam koleksi baru. Perhatikan bahwa karena utas lain di latar belakang akan mengakses koleksi ini (dalam peristiwa GamepadAdded dan GamepadRemoved ), Anda perlu menempatkan kunci di sekitar kode apa pun yang membaca atau memperbarui koleksi.

auto myGamepads = ref new Vector<Gamepad^>();
critical_section myLock{};

for (auto gamepad : Gamepad::Gamepads)
{
    // Check if the gamepad is already in myGamepads; if it isn't, add it.
    critical_section::scoped_lock lock{ myLock };
    auto it = std::find(begin(myGamepads), end(myGamepads), gamepad);

    if (it == end(myGamepads))
    {
        // This code assumes that you're interested in all gamepads.
        myGamepads->Append(gamepad);
    }
}
private readonly object myLock = new object();
private List<Gamepad> myGamepads = new List<Gamepad>();
private Gamepad mainGamepad;

private void GetGamepads()
{
    lock (myLock)
    {
        foreach (var gamepad in Gamepad.Gamepads)
        {
            // Check if the gamepad is already in myGamepads; if it isn't, add it.
            bool gamepadInList = myGamepads.Contains(gamepad);

            if (!gamepadInList)
            {
                // This code assumes that you're interested in all gamepads.
                myGamepads.Add(gamepad);
            }
        }
    }   
}

Menambahkan dan menghapus gamepad

Saat gamepad ditambahkan atau dihapus, peristiwa GamepadAdded dan GamepadRemoved akan dimunculkan. Anda dapat mendaftarkan handler untuk peristiwa ini untuk melacak gamepad yang saat ini terhubung.

Contoh berikut mulai melacak gamepad yang telah ditambahkan.

Gamepad::GamepadAdded += ref new EventHandler<Gamepad^>(Platform::Object^, Gamepad^ args)
{
    // Check if the just-added gamepad is already in myGamepads; if it isn't, add
    // it.
    critical_section::scoped_lock lock{ myLock };
    auto it = std::find(begin(myGamepads), end(myGamepads), args);

    if (it == end(myGamepads))
    {
        // This code assumes that you're interested in all new gamepads.
        myGamepads->Append(args);
    }
}
Gamepad.GamepadAdded += (object sender, Gamepad e) =>
{
    // Check if the just-added gamepad is already in myGamepads; if it isn't, add
    // it.
    lock (myLock)
    {
        bool gamepadInList = myGamepads.Contains(e);

        if (!gamepadInList)
        {
            myGamepads.Add(e);
        }
    }
};

Contoh berikut berhenti melacak gamepad yang telah dihapus. Anda juga harus menangani apa yang terjadi pada gamepad yang Anda lacak saat dihapus; misalnya, kode ini hanya melacak input dari satu gamepad, dan hanya mengaturnya ke nullptr saat dihapus. Anda harus memeriksa setiap bingkai jika gamepad Anda aktif, dan memperbarui gamepad mana yang Anda kumpulkan inputnya ketika pengontrol terhubung dan terputus.

Gamepad::GamepadRemoved += ref new EventHandler<Gamepad^>(Platform::Object^, Gamepad^ args)
{
    unsigned int indexRemoved;
    critical_section::scoped_lock lock{ myLock };

    if(myGamepads->IndexOf(args, &indexRemoved))
    {
        if (m_gamepad == myGamepads->GetAt(indexRemoved))
        {
            m_gamepad = nullptr;
        }

        myGamepads->RemoveAt(indexRemoved);
    }
}
Gamepad.GamepadRemoved += (object sender, Gamepad e) =>
{
    lock (myLock)
    {
        int indexRemoved = myGamepads.IndexOf(e);

        if (indexRemoved > -1)
        {
            if (mainGamepad == myGamepads[indexRemoved])
            {
                mainGamepad = null;
            }

            myGamepads.RemoveAt(indexRemoved);
        }
    }
};

Lihat Praktik input untuk game untuk informasi selengkapnya.

Pengguna dan headset

Setiap gamepad dapat dikaitkan dengan akun pengguna untuk menautkan identitas mereka ke gameplay mereka, dan dapat memiliki headset yang terpasang untuk memfasilitasi obrolan suara atau fitur dalam game. Untuk mempelajari selengkapnya tentang bekerja dengan pengguna dan headset, lihat Melacak pengguna dan perangkat dan Headset mereka.

Membaca gamepad

Setelah mengidentifikasi gamepad yang Anda minati, Anda siap untuk mengumpulkan input darinya. Namun, tidak seperti beberapa jenis input lain yang mungkin biasa Anda gunakan, gamepad tidak mengkomunikasikan perubahan status dengan menaikkan peristiwa. Sebagai gantinya, Anda mengambil pembacaan rutin tentang status mereka saat ini dengan polling mereka.

Polling gamepad

Polling menangkap rekam jepret perangkat navigasi pada titik waktu yang tepat. Pendekatan pengumpulan input ini cocok untuk sebagian besar game karena logika mereka biasanya berjalan dalam perulangan deterministik daripada digerakkan oleh peristiwa; biasanya juga lebih mudah untuk menginterpretasikan perintah game dari input yang dikumpulkan sekaligus daripada dari banyak input tunggal yang dikumpulkan dari waktu ke waktu.

Anda melakukan polling gamepad dengan memanggil GetCurrentReading; fungsi ini mengembalikan GamepadReading yang berisi status gamepad.

Contoh berikut melakukan polling gamepad untuk statusnya saat ini.

auto gamepad = myGamepads[0];

GamepadReading reading = gamepad->GetCurrentReading();
Gamepad gamepad = myGamepads[0];

GamepadReading reading = gamepad.GetCurrentReading();

Selain status gamepad, setiap pembacaan mencakup tanda waktu yang menunjukkan dengan tepat ketika status diambil. Tanda waktu berguna untuk berkaitan dengan waktu pembacaan sebelumnya atau waktu simulasi permainan.

Membaca thumbstick

Setiap thumbstick menyediakan pembacaan analog antara -1.0 dan +1.0 di sumbu X dan Y. Di sumbu X, nilai -1.0 sesuai dengan posisi thumbstick paling kiri; nilai +1.0 sesuai dengan posisi paling kanan. Dalam sumbu Y, nilai -1.0 sesuai dengan posisi thumbstick paling bawah; nilai +1.0 sesuai dengan posisi paling atas. Di kedua sumbu, nilainya sekitar 0,0 ketika tongkat berada di posisi tengah, tetapi normal bagi nilai yang tepat untuk bervariasi, bahkan di antara pembacaan berikutnya; strategi untuk mengurangi variasi ini dibahas nanti di bagian ini.

Nilai sumbu X thumbstick kiri dibaca dari LeftThumbstickXproperti struktur GamepadReading ; nilai sumbu Y dibaca dari LeftThumbstickY properti . Nilai sumbu X thumbstick kanan dibaca dari RightThumbstickX properti ; nilai sumbu Y dibaca dari RightThumbstickY properti .

float leftStickX = reading.LeftThumbstickX;   // returns a value between -1.0 and +1.0
float leftStickY = reading.LeftThumbstickY;   // returns a value between -1.0 and +1.0
float rightStickX = reading.RightThumbstickX; // returns a value between -1.0 and +1.0
float rightStickY = reading.RightThumbstickY; // returns a value between -1.0 and +1.0
double leftStickX = reading.LeftThumbstickX;   // returns a value between -1.0 and +1.0
double leftStickY = reading.LeftThumbstickY;   // returns a value between -1.0 and +1.0
double rightStickX = reading.RightThumbstickX; // returns a value between -1.0 and +1.0
double rightStickY = reading.RightThumbstickY; // returns a value between -1.0 and +1.0

Saat membaca nilai thumbstick, Anda akan melihat bahwa mereka tidak menghasilkan pembacaan netral 0,0 dengan andal ketika thumbstick tidak aktif di posisi tengah; sebaliknya, mereka akan menghasilkan nilai yang berbeda mendekati 0,0 setiap kali thumbstick dipindahkan dan dikembalikan ke posisi tengah. Untuk mengurangi variasi ini, Anda dapat menerapkan deadzone kecil, yang merupakan rentang nilai di dekat posisi pusat ideal yang diabaikan. Salah satu cara untuk menerapkan deadzone adalah dengan menentukan seberapa jauh dari tengah thumbstick telah bergerak, dan mengabaikan pembacaan yang lebih dekat dari beberapa jarak yang Anda pilih. Anda dapat menghitung jarak secara kasar—itu tidak tepat karena pembacaan thumbstick pada dasarnya polar, bukan planar, nilai—hanya dengan menggunakan teori Pythagorean. Ini menghasilkan deadzone radial.

Contoh berikut menunjukkan deadzone radial dasar menggunakan teori Pythagorean.

float leftStickX = reading.LeftThumbstickX;   // returns a value between -1.0 and +1.0
float leftStickY = reading.LeftThumbstickY;   // returns a value between -1.0 and +1.0

// choose a deadzone -- readings inside this radius are ignored.
const float deadzoneRadius = 0.1;
const float deadzoneSquared = deadzoneRadius * deadzoneRadius;

// Pythagorean theorem -- for a right triangle, hypotenuse^2 = (opposite side)^2 + (adjacent side)^2
auto oppositeSquared = leftStickY * leftStickY;
auto adjacentSquared = leftStickX * leftStickX;

// accept and process input if true; otherwise, reject and ignore it.
if ((oppositeSquared + adjacentSquared) > deadzoneSquared)
{
    // input accepted, process it
}
double leftStickX = reading.LeftThumbstickX;   // returns a value between -1.0 and +1.0
double leftStickY = reading.LeftThumbstickY;   // returns a value between -1.0 and +1.0

// choose a deadzone -- readings inside this radius are ignored.
const double deadzoneRadius = 0.1;
const double deadzoneSquared = deadzoneRadius * deadzoneRadius;

// Pythagorean theorem -- for a right triangle, hypotenuse^2 = (opposite side)^2 + (adjacent side)^2
double oppositeSquared = leftStickY * leftStickY;
double adjacentSquared = leftStickX * leftStickX;

// accept and process input if true; otherwise, reject and ignore it.
if ((oppositeSquared + adjacentSquared) > deadzoneSquared)
{
    // input accepted, process it
}

Setiap thumbstick juga bertindak sebagai tombol saat ditekan ke dalam; untuk informasi selengkapnya tentang membaca input ini, lihat Membaca tombol.

Membaca pemicu

Pemicu dinyatakan sebagai nilai floating-point antara 0,0 (dirilis sepenuhnya) dan 1,0 (sepenuhnya tertekan). Nilai pemicu kiri dibaca dari LeftTrigger properti struktur GamepadReading ; nilai pemicu kanan dibaca dari RightTrigger properti .

float leftTrigger  = reading.LeftTrigger;  // returns a value between 0.0 and 1.0
float rightTrigger = reading.RightTrigger; // returns a value between 0.0 and 1.0
double leftTrigger = reading.LeftTrigger;  // returns a value between 0.0 and 1.0
double rightTrigger = reading.RightTrigger; // returns a value between 0.0 and 1.0

Membaca tombol

Masing-masing tombol gamepad—empat arah D-pad, bumper kiri dan kanan, tekan thumbstick kiri dan kanan, A, B, X, Y, View, dan Menu—menyediakan pembacaan digital yang menunjukkan apakah itu ditekan (ke bawah) atau dilepaskan (ke atas). Untuk efisiensi, pembacaan tombol tidak direpresentasikan sebagai nilai boolean individual; sebaliknya, mereka semua dikemas ke dalam satu bitfield yang diwakili oleh enumerasi GamepadButtons .

Nilai tombol dibaca dari Buttons properti struktur GamepadReading . Karena properti ini adalah bitfield, masking bitwise digunakan untuk mengisolasi nilai tombol yang Anda minati. Tombol ditekan (ke bawah) ketika bit yang sesuai diatur; jika tidak, itu dirilis (naik).

Contoh berikut menentukan apakah tombol A ditekan.

if (GamepadButtons::A == (reading.Buttons & GamepadButtons::A))
{
    // button A is pressed
}
if (GamepadButtons.A == (reading.Buttons & GamepadButtons.A))
{
    // button A is pressed
}

Contoh berikut menentukan apakah tombol A dirilis.

if (GamepadButtons::None == (reading.Buttons & GamepadButtons::A))
{
    // button A is released
}
if (GamepadButtons.None == (reading.Buttons & GamepadButtons.A))
{
    // button A is released
}

Terkadang Anda mungkin ingin menentukan kapan tombol beralih dari ditekan ke dilepaskan atau dilepaskan untuk ditekan, apakah beberapa tombol ditekan atau dilepaskan, atau jika satu set tombol disusun dengan cara tertentu—beberapa ditekan, beberapa tidak. Untuk informasi tentang cara mendeteksi setiap kondisi ini, lihat Mendeteksi transisi tombol dan Mendeteksi pengaturan tombol yang kompleks.

Jalankan sampel input gamepad

Sampel GamepadUWP (github) menunjukkan cara terhubung ke gamepad dan membaca statusnya.

Gambaran umum pemicu getaran dan impuls

Motor getaran di dalam gamepad adalah untuk memberikan umpan balik taktil kepada pengguna. Game menggunakan kemampuan ini untuk menciptakan rasa perendaman yang lebih besar, untuk membantu mengomunikasikan informasi status (seperti mengambil kerusakan), untuk memberi sinyal kedekatan dengan objek penting, atau untuk kegunaan kreatif lainnya.

Gamepad Xbox One dilengkapi dengan total empat motor getaran independen. Dua adalah motor besar yang terletak di bodi gamepad; motor kiri memberikan getaran kasar dan amplitude tinggi, sedangkan motor kanan memberikan getaran yang lebih lembut dan lebih halus. Dua lainnya adalah motor kecil, satu di dalam setiap pemicu, yang memberikan semburan getaran tajam langsung ke jari-jari pemicu pengguna; kemampuan unik dari gamepad Xbox One ini adalah alasan pemicunya disebut sebagai pemicu impuls. Dengan mengatur motor-motor ini bersama-sama, berbagai sensasi taktil dapat diproduksi.

Menggunakan getaran dan impuls

Getaran Gamepad dikontrol melalui properti Getaran kelas Gamepad . Vibration adalah instans struktur GamepadVibration yang terdiri dari empat nilai floating point; setiap nilai mewakili intensitas salah satu motor.

Meskipun anggota Gamepad.Vibration properti dapat dimodifikasi secara langsung, disarankan agar Anda menginisialisasi instans terpisah GamepadVibration ke nilai yang Anda inginkan, lalu menyalinnya ke Gamepad.Vibration properti untuk mengubah intensitas motor yang sebenarnya sekaligus.

Contoh berikut menunjukkan cara mengubah intensitas motor sekaligus.

// get the first gamepad
Gamepad^ gamepad = Gamepad::Gamepads->GetAt(0);

// create an instance of GamepadVibration
GamepadVibration vibration;

// ... set vibration levels on vibration struct here

// copy the GamepadVibration struct to the gamepad
gamepad.Vibration = vibration;
// get the first gamepad
Gamepad gamepad = Gamepad.Gamepads[0];

// create an instance of GamepadVibration
GamepadVibration vibration = new GamepadVibration();

// ... set vibration levels on vibration struct here

// copy the GamepadVibration struct to the gamepad
gamepad.Vibration = vibration;

Menggunakan motor getaran

Motor getaran kiri dan kanan mengambil nilai titik pecahan antara 0,0 (tanpa getaran) dan 1,0 (getaran paling intens). Intensitas motor kiri diatur oleh LeftMotor properti struktur GamepadVibration ; intensitas motor kanan diatur oleh RightMotor properti.

Contoh berikut mengatur intensitas motor getaran dan mengaktifkan getaran gamepad.

GamepadVibration vibration;
vibration.LeftMotor = 0.80;  // sets the intensity of the left motor to 80%
vibration.RightMotor = 0.25; // sets the intensity of the right motor to 25%
gamepad.Vibration = vibration;
GamepadVibration vibration = new GamepadVibration();
vibration.LeftMotor = 0.80;  // sets the intensity of the left motor to 80%
vibration.RightMotor = 0.25; // sets the intensity of the right motor to 25%
mainGamepad.Vibration = vibration;

Ingatlah bahwa kedua motor ini tidak identik sehingga mengatur properti ini ke nilai yang sama tidak menghasilkan getaran yang sama dalam satu motor seperti di motor lainnya. Untuk nilai apa pun, motor kiri menghasilkan getaran yang lebih kuat pada frekuensi yang lebih rendah daripada motor kanan yang—untuk nilai yang sama—menghasilkan getaran yang lebih lembut pada frekuensi yang lebih tinggi. Bahkan pada nilai maksimum, motor kiri tidak dapat menghasilkan frekuensi tinggi motor kanan, motor kanan juga tidak dapat menghasilkan kekuatan tinggi motor kiri. Namun, karena motornya kaku dihubungkan oleh bodi gamepad, pemain tidak mengalami getaran sepenuhnya secara mandiri meskipun motor memiliki karakteristik yang berbeda dan dapat bergetar dengan intensitas yang berbeda. Pengaturan ini memungkinkan rentang sensasi yang lebih luas dan lebih ekspresif untuk diproduksi daripada jika motor identik.

Menggunakan pemicu impuls

Setiap motor pemicu impuls mengambil nilai floating point antara 0,0 (tanpa getaran) dan 1,0 (getaran paling intens). Intensitas motor pemicu kiri diatur oleh LeftTrigger properti struktur GamepadVibration ; intensitas pemicu kanan diatur oleh RightTrigger properti .

Contoh berikut menetapkan intensitas pemicu impuls dan mengaktifkannya.

GamepadVibration vibration;
vibration.LeftTrigger = 0.75;  // sets the intensity of the left trigger to 75%
vibration.RightTrigger = 0.50; // sets the intensity of the right trigger to 50%
gamepad.Vibration = vibration;
GamepadVibration vibration = new GamepadVibration();
vibration.LeftTrigger = 0.75;  // sets the intensity of the left trigger to 75%
vibration.RightTrigger = 0.50; // sets the intensity of the right trigger to 50%
mainGamepad.Vibration = vibration;

Tidak seperti yang lain, dua motor getaran di dalam pemicu identik sehingga menghasilkan getaran yang sama di salah satu motor untuk nilai yang sama. Namun, karena motor ini tidak terhubung dengan kaku dengan cara apa pun, pemain mengalami getaran secara independen. Pengaturan ini memungkinkan sensasi yang sepenuhnya independen diarahkan ke kedua pemicu secara bersamaan, dan membantu mereka menyampaikan informasi yang lebih spesifik daripada motor di bodi gamepad.

Menjalankan sampel getaran gamepad

Sampel GamepadVibrationUWP (github) menunjukkan bagaimana motor getaran gamepad dan pemicu impuls digunakan untuk menghasilkan berbagai efek.

Baca juga