Building a Plattformer - Bagaimana menentukan apakah pemain diizinkan melompat?

16

Saya sedang membangun game Plattformer Jump n 'Run Style yang sederhana. Saya tidak menggunakan ubin - sebagai gantinya saya memiliki bentuk geometris untuk entitas level saya (dan pemain juga satu). Saya menyelesaikan kode deteksi tabrakan saya dan semuanya berfungsi dengan baik sejauh ini.

Selanjutnya, saya ingin menerapkan jumping. Hanya memeriksa apakah pemain menekan tombol yang sesuai dan menambahkan beberapa kecepatan ke atas. Bekerja dengan baik. Tapi itu bekerja bahkan jika pemain di udara, yang bukan yang saya inginkan. ;-)

Jadi, saya harus memeriksa apakah pemainnya berdiri pada sesuatu. Ide pertama saya adalah untuk memeriksa apakah ada tabrakan di frame terakhir dan menandai pemain sebagai "mampu melompat", tetapi ini bahkan akan memicu jika pemain menabrak dinding di udara. Karena kemampuan matematika saya tidak begitu baik, saya meminta bantuan - bahkan petunjuk akan melakukan cara mengimplementasikannya.

Terima kasih!

Malax
sumber

Jawaban:

14

Dua pilihan muncul dalam pikiran:

  • Pikiran pertama adalah menandai geometri dengan ID dan kemudian memeriksa untuk melihat apakah tabrakan dengan geometri ditandai sebagai lantai. Ini menawarkan kontrol permukaan melompat paling banyak tetapi dengan biaya dalam waktu pembuatan tingkat.
  • Kedua adalah untuk memeriksa normal tabrakan, jika itu mengarah ke atas maka biarkan melompat. Atau dalam beberapa margin ke atas, tergantung pada apakah Anda memiliki lantai miring. Ini fleksibel dan tidak memerlukan penandaan, tetapi jika Anda memiliki lantai dan dinding yang miring, Anda mungkin akan melompat di tempat yang tidak Anda inginkan.
wkerslake
sumber
Cek yang normal sepertinya ide bagus bagi saya. Terima kasih telah memposting itu.
Christopher Horenstein
+1 untuk pemeriksaan normal. Ini memastikan bahwa lantai dapat dengan mulus beralih ke dinding tanpa menyebabkan masalah.
Soviut
1
+1 Pasti memeriksa tabrakan-normal. Memiliki berbagai jenis dunia-geometri baik-baik saja dan memungkinkan untuk desain tingkat yang lebih fleksibel, tetapi itu tidak menyelesaikan masalah utama Anda. "Dinding" mungkin juga bertipe "tanah", tergantung apakah Anda menabraknya atau berdiri di atasnya. Di situlah tabrakan-normal akan membantu.
bummzack
Saya menemukan solusi normal sangat bagus dan akan menyelesaikan masalah saya. Bahkan jawaban berperingkat tinggi lainnya bagus dan seperti itu - tetapi ini menjawab pertanyaan saya dengan lebih baik. Terima kasih wkerslake kamu!
Malax
8

Anda tentu harus menerapkan semacam jenis permukaan. Pikirkan itu, bagaimana Anda mengaturnya jika Anda bisa menaiki tangga jika Anda tidak tahu apakah karakter Anda hanya menabrak dinding atau tangga? Anda bisa menggunakan OOP untuk mengelola hierarki jenis menggunakan warisan, tetapi saya menyarankan Anda untuk menggunakan "kategori" yang diimplementasikan menggunakan jenis yang disebutkan:

Inilah idenya: Penghitungan "Tabrakan" memiliki bendera untuk setiap kategori. Sebagai contoh:

namespace Collisions
{
    enum Type
    {
        None   = 0,
        Floor  = 1 << 0,
        Ladder = 1 << 1,
        Enemy  = 1 << 2,
        ... // And whatever else you need.

        // Then, you can construct named groups of flags.
        Player = Floor | Ladder | Enemy
    };
}

Dengan metode ini, Anda akan dapat menguji apakah pemain juste bertabrakan dengan apa yang harus Anda kelola, sehingga mesin Anda dapat memanggil metode "Collided" dari entitas:

void Player::Collided( Collisions::Type group )
{
   if ( group & Collisions::Ladder )
   {
      // Manage Ladder Collision
   }
   if ( group & Collisions::Floor )
   {
      // Manage Floor Collision
   }
   if ( group & Collisions::Enemy )
   {
      // Manage Enemy Collision
   }
}

Metode ini menggunakan bendera bitwise dan operator "Atau" bitwise untuk memastikan setiap kelompok memiliki nilai yang berbeda, berdasarkan nilai biner kategori. Metode ini berfungsi dengan baik dan mudah diskalakan sehingga Anda dapat membuat grup tabrakan pabean. Setiap Entitas (Pemain, Musuh, dll.) Di gim Anda memiliki beberapa bit yang disebut "filter", yang digunakan untuk menentukan apa yang dapat bertabrakan dengannya. Kode tumbukan Anda harus memeriksa untuk melihat apakah bit cocok dan bereaksi sesuai, dengan beberapa kode yang mungkin terlihat seperti:

void PhysicEngine::OnCollision(...)
{
    mPhysics.AddContact( body1, body1.GetFilter(), body2, body2.GetFilter() );
}
Frédérick Imbeault
sumber
Apakah ada alasan untuk menggunakan const int bukan enum? Dalam kasus degenerasi dari satu flag atau grup bernama, tipe yang disebutkan lebih mudah dibaca, dan Anda mendapatkan kemampuan pengecekan tipe tambahan dalam kebanyakan kompiler.
Ya, enum tidak bisa (saya pikir) menggunakan operator biner untuk membuat grup kustom baru. Alasan mengapa saya membuat tesis ini adalah karena saya menggunakan kelas Config dengan anggota statis publik untuk alasan keterbacaan, menjaga keamanan parameter konfigurasi.
Frédérick Imbeault
Bisa. Enumerasi "rvalues" dapat berupa operasi bitwise pada nilai konstan lainnya (saya pikir itu bisa berupa ekspresi konstan sebenarnya, tapi saya terlalu malas untuk memeriksa standar). Nilai-nilai non-statis dicapai dengan cara yang persis sama dengan contoh Anda, tetapi diketik dengan lebih akurat. Saya tidak tahu apa yang Anda maksud dengan "keamanan", tetapi sintaks yang persis sama tanpa overhead yang terkait dengan kelas dapat dicapai dengan namespace.
Saya memperbarui contoh kode Anda untuk menggunakan tipe enumerasi.
Mungkin itu membuat berpikir lebih mudah untuk dibaca. Saya menyetujui perubahan yang Anda buat, metode yang saya gunakan benar tetapi saya belum menyesuaikan kode untuk penjelasan enouph, terima kasih atas koreksinya. Untuk namespace, Anda benar, tetapi ini tidak berlaku untuk semua bahasa (Bahasa scripting untuk contoh). "Keamanan" adalah bahwa opsi konfigurasi saya statis sehingga tidak dapat dimodifikasi tetapi menggunakan enum untuk mencegahnya juga.
Frédérick Imbeault
1

Jika Anda menganggap kaki karakter Anda sebagai alway di bawah karakter dengan jarak tertentu, dan jika Anda tidak bergerak menjauh dari permukaan maka karakter Anda ada di tanah.

Dalam pseudo-code kasar:

bool isOnGround(Character& chr)
{
   // down vector is opposite from your characters current up vector.
   // if you want to support wall jumps, animate the vecToFoot as appropriate.
   vec vecToFoot = -chr.up * chr.footDistanceFromCentre;

// if feet are moving away from any surface, can't be on the ground if (dot(chr.velocity, down) < 0.0f) return false;

// generate line from character centre to the foot position vec linePos0 = chr.position; vec linePos1 = chr.position + vecToFoot;

// test line against world. If it returns a hit surface, we're on the ground if (testLineAgainstWorld(line0, line1, &surface) return true;

return false; }

jpaver
sumber
Ini tidak akan berfungsi jika Anda ingin menambahkan fitur seperti lompatan ganda, atau lompatan di dinding saya rasa?
Frédérick Imbeault
Saya tidak percaya OP menyebutkan lompatan ganda atau lompatan dinding, bukan?
jpaver
Saya telah merevisi kode untuk menunjukkan cara abstrak vektor ke kaki Anda dari pusat karakter. Jika Anda menghidupkan ini, dan kaki berakhir ke samping dan menabrak dinding, Anda akan dapat mendukung lompatan dinding.
jpaver
Ini pada dasarnya adalah cara yang kurang fleksibel untuk memeriksa tabrakan normal. (Kurang fleksibel karena Anda dapat dengan mudah membedakan lompatan dinding dari lompatan lantai dari lompatan langit-langit hanya dengan memeriksa arah normal.)
2
Jangan pernah memanggil variabel char, bahkan dalam pseudo-code.
sayap kanan