Menerapkan Template Status dalam Persatuan

gambar

Dalam proses pemrograman entitas dalam game, situasi muncul ketika mereka harus bertindak dalam kondisi yang berbeda dengan cara yang berbeda, yang menunjukkan penggunaan negara .

Tetapi jika Anda memutuskan untuk menggunakan kekerasan, kode akan dengan cepat berubah menjadi kekacauan kusut dengan banyak pernyataan if-else bersarang.

Untuk solusi anggun untuk masalah ini, Anda dapat menggunakan pola desain State. Kami akan mendedikasikan tutorial ini untuknya!

Dari tutorial Anda:

  • Pelajari dasar-dasar templat Negara di Unity.
  • Anda akan belajar apa itu mesin negara dan kapan menggunakannya.
  • Pelajari cara menggunakan konsep-konsep ini untuk mengontrol pergerakan karakter Anda.

Catatan : tutorial ini untuk pengguna tingkat lanjut; diasumsikan bahwa Anda sudah tahu cara bekerja di Unity dan memiliki tingkat rata-rata pengetahuan C #. Selain itu, tutorial ini menggunakan Unity 2019.2 dan C # 7.

Mulai bekerja


Unduh materi proyek . Buka zip file zip dan buka proyek starter di Unity.

Ada beberapa folder dalam proyek yang akan membantu Anda memulai. Folder Aset / RW berisi folder Animasi , Bahan , Model , Rak itan , Sumber Daya , Adegan , Skrip, dan Suara , dinamai sesuai dengan sumber daya yang dikandungnya.

Untuk menyelesaikan tutorial, kami hanya akan bekerja dengan Adegan dan Skrip .

Pergi ke RW / Adegan dan buka Main . Dalam mode Game, Anda akan melihat karakter di tudung di dalam kastil abad pertengahan.


Klik Play dan perhatikan bagaimana Kamera bergerak agar sesuai dengan bingkai Karakter . Saat ini, dalam permainan kecil kami tidak ada interaksi, kami akan mengerjakannya dalam tutorial.


Jelajahi karakter


Dalam hierarki, pilih Karakter . Periksa Inspektur . Anda akan melihat komponen dengan nama yang sama berisi logika kontrol karakter .


Buka Character.cs yang terletak di RW / Script .

Script melakukan banyak tindakan, tetapi sebagian besar tidak penting bagi kami. Untuk saat ini, mari kita perhatikan metode-metode berikut.

  • Move : bergerak karakter, menerima nilai speed tipe float sebagai kecepatan gerakan dan rotationSpeed , Kecepatan sebagai kecepatan sudut.
  • ResetMoveParams : metode ini mengatur ulang parameter yang digunakan untuk menghidupkan gerakan dan kecepatan sudut karakter. Ini digunakan hanya untuk membersihkan.
  • SetAnimationBool : Ini mengatur param animasi param dari tipe Bool ke nilai.
  • CheckCollisionOverlap : menerima bertipe Vector3 dan mengembalikan bool yang menentukan apakah ada colliders dalam radius yang ditentukan dari .
  • TriggerAnimation : TriggerAnimation parameter animasi parameter input.
  • ApplyImpulse : ApplyImpulse pulsa ke Karakter sama dengan force parameter input tipe Vector3 .

Di bawah ini Anda akan melihat metode ini. Dalam tutorial kami, konten dan pekerjaan internal mereka tidak penting.

Apa itu mesin negara


Mesin negara adalah konsep di mana sebuah wadah menyimpan keadaan sesuatu pada saat tertentu. Berdasarkan data input, ini dapat memberikan kesimpulan tergantung pada kondisi saat ini, meneruskan proses ini ke status baru. Mesin negara dapat direpresentasikan sebagai diagram keadaan . Mempersiapkan diagram keadaan memungkinkan Anda untuk memikirkan semua kondisi sistem yang mungkin dan transisi di antara mereka.

Mesin negara


Mesin negara terbatas atau FSM (mesin negara terbatas) adalah salah satu dari empat keluarga utama mesin . Automata adalah model abstrak dari mesin sederhana. Mereka dipelajari dalam kerangka teori automata - cabang teori ilmu komputer.

Singkatnya:

  • FSM terdiri dari sejumlah kondisi yang terbatas. Pada waktu tertentu , hanya satu dari kondisi ini yang aktif .
  • Setiap negara menentukan ke negara mana ia akan masuk sebagai output berdasarkan urutan yang diterima dari informasi yang masuk .
  • Status output menjadi status aktif baru. Dengan kata lain, ada transisi antar negara .


Untuk lebih memahami hal ini, pertimbangkan karakter permainan platform yang ada di tanah. Karakter dalam status Berdiri . Ini akan menjadi status aktifnya hingga pemain menekan tombol sehingga karakter melompat.

Status Berdiri mengidentifikasi tombol yang ditekan sebagai input signifikan dan, sebagai output , beralih ke status Jumping .

Misalkan ada sejumlah keadaan gerak tertentu dan satu karakter hanya dapat berada di salah satu keadaan pada suatu waktu. Ini adalah contoh FSM.

Mesin negara hirarkis


Pertimbangkan platformer menggunakan FSM, di mana beberapa negara bagian berbagi logika fisika umum. Misalnya, Anda bisa bergerak dan melompat di status Crouching dan Standing . Dalam hal ini, beberapa variabel yang masuk mengarah ke perilaku yang sama dan keluaran informasi untuk dua keadaan yang berbeda.

Dalam situasi seperti itu, adalah logis untuk mendelegasikan perilaku umum ke beberapa negara lain. Untungnya, ini dapat dicapai dengan menggunakan mesin negara hierarkis .

Dalam FSM hirarkis, ada substate mendelegasikan informasi masuk mentah ke substrat mereka. Ini pada gilirannya memungkinkan Anda untuk dengan anggun mengurangi ukuran dan kompleksitas FSM, sambil mempertahankan logikanya.

Template Status


Dalam buku mereka Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson dan John Vlissidis ( Geng Empat ) mendefinisikan tugas templat Negara sebagai berikut:

โ€œDia harus membiarkan objek mengubah perilakunya ketika keadaan internalnya berubah. Dalam hal ini, akan terlihat bahwa objek telah mengubah kelasnya. "

Untuk lebih memahami ini, perhatikan contoh berikut:

  • Sebuah skrip yang menerima informasi yang masuk untuk logika pergerakan dilampirkan ke entitas dalam game.
  • Kelas ini menyimpan variabel status saat ini yang hanya merujuk ke instance kelas negara .
  • Informasi yang masuk didelegasikan ke keadaan saat ini, yang memprosesnya dan menciptakan perilaku yang didefinisikan dalam dirinya sendiri. Ini juga menangani transisi negara yang diperlukan.

Oleh karena itu, karena fakta bahwa pada waktu yang berbeda, variabel state saat ini merujuk ke state yang berbeda, akan terlihat bahwa kelas skrip yang sama berperilaku berbeda. Ini adalah inti dari templat "Status".

Dalam proyek kami, kelas Karakter yang disebutkan di atas akan berperilaku berbeda tergantung pada keadaan yang berbeda. Tapi kita butuh dia untuk berperilaku sendiri!


Dalam kasus umum, ada tiga poin utama untuk setiap kelas negara yang memungkinkan perilaku negara secara keseluruhan:

  • Entri : ini adalah saat ketika entitas memasuki negara dan melakukan tindakan yang perlu dilakukan hanya sekali ketika memasuki negara.
  • Keluar : mirip dengan input - semua operasi reset dilakukan di sini, yang harus dilakukan hanya sebelum keadaan berubah.
  • Perbarui Loop : Berikut adalah logika pembaruan dasar yang berjalan di setiap frame. Ini dapat dibagi menjadi beberapa bagian, misalnya, siklus untuk memperbarui fisika dan siklus untuk memproses input pemain.


Mendefinisikan state and state machine


Pergi ke RW / Script dan buka StateMachine.cs .

Mesin Negara , seperti yang Anda duga, memberikan abstraksi untuk mesin negara. Perhatikan bahwa CurrentState terletak dengan benar di dalam kelas ini. Ini akan menyimpan tautan ke status mesin aktif saat ini.

Sekarang, untuk mendefinisikan konsep negara , mari kita pergi ke RW / Script dan buka script State.cs di IDE.

State adalah kelas abstrak yang akan kita gunakan sebagai model dari mana semua kelas status proyek diturunkan. Sebagian kode dalam materi proyek sudah siap.

DisplayOnUI hanya menampilkan nama status saat ini di UI di layar. Anda tidak perlu mengetahui perangkat internalnya, cukup pahami bahwa ia menerima enumerator dari jenis UIManager.Alignment sebagai parameter input, yang bisa Left atau Right . Tampilan nama status di kiri bawah atau kanan layar tergantung padanya.

Selain itu, ada dua variabel yang dilindungi, character dan stateMachine . Variabel character merujuk ke turunan dari kelas Karakter , dan stateMachine merujuk ke turunan dari mesin keadaan yang terkait dengan keadaan.

Saat membuat instance keadaan, konstruktor mengikat character dan stateMachine .

Masing-masing dari banyak contoh Character dalam sebuah adegan dapat memiliki set negara dan mesin negara sendiri.

Sekarang tambahkan metode berikut ke State.cs dan simpan file:

 public virtual void Enter() { DisplayOnUI(UIManager.Alignment.Left); } public virtual void HandleInput() { } public virtual void LogicUpdate() { } public virtual void PhysicsUpdate() { } public virtual void Exit() { } 

Metode virtual ini menentukan poin status kunci yang dijelaskan di atas. Ketika mesin status melakukan transisi antar status, kami memanggil Exit untuk status sebelumnya dan Enter status aktif baru.

HandleInput , LogicUpdate dan PhysicsUpdate bersama-sama menentukan loop pembaruan . HandleInput menangani input pemain. LogicUpdate memproses logika dasar, sementara PhyiscsUpdate memproses perhitungan logika dan fisika.

Sekarang buka StateMachine.cs lagi, tambahkan metode berikut dan simpan file:

 public void Initialize(State startingState) { CurrentState = startingState; startingState.Enter(); } public void ChangeState(State newState) { CurrentState.Exit(); CurrentState = newState; newState.Enter(); } 

Initialize mengkonfigurasi mesin keadaan dengan mengatur CurrentState untuk CurrentState dan memanggil Enter untuk itu. Ini menginisialisasi mesin keadaan, untuk pertama kalinya mengatur keadaan aktif.

ChangeState menangani transisi keadaan . Itu panggilan Exit untuk CurrentState lama sebelum mengganti referensi dengan newState . Pada akhirnya, ia memanggil Enter untuk newState .

Jadi, kami mengatur negara dan mesin negara .

Menciptakan kondisi gerak


Lihatlah diagram keadaan berikut, yang menunjukkan berbagai kondisi pergerakan esensi dalam gim pemain. Di bagian ini, kami menerapkan template "Status" untuk gerakan yang ditunjukkan pada gambar FSM :


Perhatikan status gerakan, yaitu Standing , Ducking dan Jumping , serta bagaimana data yang masuk menyebabkan transisi antar negara. Ini adalah FSM hierarkis di mana Grounded adalah sub-negara bagian untuk sub-state Ducking dan Standing .

Kembali ke Unity dan pergi ke RW / Scripts / States . Di sana Anda akan menemukan beberapa file C # dengan nama yang berakhir dengan Negara .

Masing-masing file ini mendefinisikan satu kelas, yang masing-masing diwarisi dari State . Oleh karena itu, kelas-kelas ini mendefinisikan status yang akan kita gunakan dalam proyek.

Sekarang buka Character.cs dari folder RW / Scripts .

Gulir di atas file #region Variables dan tambahkan kode berikut:

 public StateMachine movementSM; public StandingState standing; public DuckingState ducking; public JumpingState jumping; 

movementSM ini mengacu pada mesin keadaan yang memproses logika gerak untuk instance Character . Kami juga menambahkan tautan ke tiga negara bagian yang kami terapkan untuk setiap jenis gerakan.

Pergi ke #region MonoBehaviour Callbacks dalam file yang sama. Tambahkan metode MonoBehaviour berikut dan kemudian simpan

 private void Start() { movementSM = new StateMachine(); standing = new StandingState(this, movementSM); ducking = new DuckingState(this, movementSM); jumping = new JumpingState(this, movementSM); movementSM.Initialize(standing); } private void Update() { movementSM.CurrentState.HandleInput(); movementSM.CurrentState.LogicUpdate(); } private void FixedUpdate() { movementSM.CurrentState.PhysicsUpdate(); } 

  • Dalam Start kode menciptakan turunan dari Mesin Negara dan menugaskannya untuk movementSM , dan juga instantiates berbagai gerak negara. Saat membuat setiap status gerakan, kami meneruskan referensi ke instance Character menggunakan this , serta instance movementSM . Pada akhirnya, kami memanggil Initialize untuk movementSM dan lulus Standing sebagai keadaan awal.
  • Dalam metode Update , kami memanggil HandleInput dan LogicUpdate untuk CurrentState dari mesin movementSM . Demikian pula, di FixedUpdate kami memanggil PhysicsUpdate untuk CurrentState dari mesin movementSM . Intinya, ini mendelegasikan tugas ke keadaan aktif; ini adalah arti dari templat โ€œStatusโ€.

Sekarang kita perlu mengatur perilaku di dalam masing-masing kondisi gerak. Bersiaplah, akan ada banyak kode!

Perusahaan yang Berdiri


Kembali ke RW / Script / Negara di jendela Proyek.

Buka Grounded.cs dan perhatikan bahwa kelas ini memiliki konstruktor yang cocok dengan konstruktor State . Ini logis karena kelas ini mewarisi darinya. Anda akan melihat hal yang sama di semua kelas negara bagian lainnya.

Tambahkan kode berikut:

 public override void Enter() { base.Enter(); horizontalInput = verticalInput = 0.0f; } public override void Exit() { base.Exit(); character.ResetMoveParams(); } public override void HandleInput() { base.HandleInput(); verticalInput = Input.GetAxis("Vertical"); horizontalInput = Input.GetAxis("Horizontal"); } public override void PhysicsUpdate() { base.PhysicsUpdate(); character.Move(verticalInput * speed, horizontalInput * rotationSpeed); } 

Inilah yang terjadi di sini:

  • Kami mendefinisikan kembali salah satu metode virtual yang didefinisikan di kelas induk. Untuk mempertahankan semua fungsi yang mungkin ada di induk, kami memanggil metode base dengan nama yang sama dari setiap metode yang diganti. Ini adalah templat penting yang akan terus kami gunakan.
  • Baris berikutnya, Enter set horizontalInput dan verticalInput nilai standarnya.
  • Di dalam Exit kami, seperti yang disebutkan di atas, memanggil metode ResetMoveParams untuk mengatur ulang ketika mengubah ke keadaan lain.
  • Dalam metode HandleInput , variabel horizontalInput dan verticalInput HandleInput nilai HandleInput nilai sumbu input horisontal dan vertikal. Berkat ini, pemain dapat mengontrol karakter menggunakan tombol W , A , S dan D.
  • Di PhysicsUpdate kami membuat panggilan Move , melewati variabel PhysicsUpdate horizontalInput dan verticalInput dikalikan dengan kecepatan yang sesuai. Dalam speed variabel speed kecepatan gerakan disimpan, dan dalam rotationSpeed , kecepatan sudut.

Sekarang buka Standing.cs dan perhatikan fakta bahwa ia mewarisi dari Grounded . Itu terjadi karena, seperti yang kami katakan di atas, Berdiri adalah substrat untuk Grounded . Ada berbagai cara untuk menerapkan hubungan ini, tetapi dalam tutorial ini kami menggunakan warisan.

Tambahkan metode override berikut dan simpan skrip:

 public override void Enter() { base.Enter(); speed = character.MovementSpeed; rotationSpeed = character.RotationSpeed; crouch = false; jump = false; } public override void HandleInput() { base.HandleInput(); crouch = Input.GetButtonDown("Fire3"); jump = Input.GetButtonDown("Jump"); } public override void LogicUpdate() { base.LogicUpdate(); if (crouch) { stateMachine.ChangeState(character.ducking); } else if (jump) { stateMachine.ChangeState(character.jumping); } } 

  • Di Enter kami mengonfigurasi variabel yang diwarisi dari Grounded . Menerapkan MovementSpeed dan RotationSpeed karakter ke speed dan rotationSpeed . Kemudian mereka berhubungan, masing-masing, dengan kecepatan gerakan normal dan kecepatan sudut yang dimaksudkan untuk esensi karakter.

    Selain itu, variabel untuk menyimpan input crouch dan jump diatur ulang ke false.
  • Di dalam HandleInput , variabel crouch dan jump menyimpan input pemain untuk squat dan lompatan. Jika dalam adegan Utama pemain menekan tombol Shift, squat diatur ke true. Demikian pula, pemain dapat menggunakan tombol Space untuk jump .
  • Di LogicUpdate kami memeriksa crouch dan jump variabel dari tipe bool . Jika crouch benar, maka movementSM.CurrentState berubah ke character.ducking . Jika jump benar, maka status berubah menjadi character.jumping .

Simpan dan rakit proyek, lalu klik Play . Anda dapat bergerak di sekitar layar menggunakan tombol W , A , S dan D. Jika Anda mencoba menekan Shift atau Spasi , perilaku tak terduga akan terjadi, karena status yang sesuai belum diterapkan.


Cobalah bergerak di bawah objek tabel. Anda akan melihat bahwa karena ketinggian karakter collider ini tidak mungkin. Agar karakter melakukan ini, tambahkan perilaku squat.

Kami memanjat di bawah meja


Buka skrip Ducking.cs . Perhatikan bahwa Ducking juga mewarisi dari kelas Grounded untuk alasan yang sama seperti Standing . Tambahkan metode override berikut dan simpan skrip:

 public override void Enter() { base.Enter(); character.SetAnimationBool(character.crouchParam, true); speed = character.CrouchSpeed; rotationSpeed = character.CrouchRotationSpeed; character.ColliderSize = character.CrouchColliderHeight; belowCeiling = false; } public override void Exit() { base.Exit(); character.SetAnimationBool(character.crouchParam, false); character.ColliderSize = character.NormalColliderHeight; } public override void HandleInput() { base.HandleInput(); crouchHeld = Input.GetButton("Fire3"); } public override void LogicUpdate() { base.LogicUpdate(); if (!(crouchHeld || belowCeiling)) { stateMachine.ChangeState(character.standing); } } public override void PhysicsUpdate() { base.PhysicsUpdate(); belowCeiling = character.CheckCollisionOverlap(character.transform.position + Vector3.up * character.NormalColliderHeight); } 

  • Di dalam Enter parameter yang menyebabkan matikan animasi jongkok diatur ke jongkok, yang memungkinkan animasi jongkok. Properti character.CrouchSpeed dan character.CrouchRotationSpeed diberi nilai speed dan rotation , yang mengembalikan gerakan karakter dan kecepatan sudut saat bergerak dalam squat .

    character.CrouchColliderHeight berikutnya.CrouchColliderHeight menetapkan ukuran collider karakter, yang mengembalikan tinggi collider yang diinginkan saat jongkok. Pada akhirnya, belowCeiling direset ke false.
  • Di dalam Exit parameter animasi jongkok diatur ke false. Ini menonaktifkan animasi squat. Kemudian ketinggian collider normal diatur, dikembalikan oleh character.NormalColliderHeight . character.NormalColliderHeight .
  • Di dalam HandleInput variabel menetapkan nilai input pemain. Di adegan Utama , memegang Shift set crouchHeld menjadi true.
  • Di dalam PhysicsUpdate variabel belowCeiling diberi nilai dengan melewati titik dalam format Vector3 dengan kepala objek game karakter ke metode CheckCollisionOverlap . Jika ada tabrakan di dekat titik ini, maka ini berarti bahwa karakter berada di bawah semacam langit-langit.
  • Secara internal, LogicUpdate memeriksa apakah crouchHeld atau belowCeiling benar. Jika tidak ada yang benar, maka movementSM.CurrentState berubah menjadi character.standing .

Bangun proyek dan klik Play . Sekarang Anda dapat bergerak di sekitar tempat kejadian. Jika Anda menekan Shift , karakter akan duduk dan Anda dapat bergerak dalam squat.

Anda juga bisa naik di bawah platform. Jika Anda melepaskan Shift saat berada di bawah platform, karakter akan tetap berada dalam squat hingga ia meninggalkan tempat perlindungannya.


Melambunglah!


Buka Jumping.cs . Anda akan melihat metode yang disebut Jump . Jangan khawatir tentang cara kerjanya; itu cukup untuk memahami bahwa itu digunakan sehingga karakter dapat melompat dengan memperhitungkan fisika dan animasi.

Sekarang tambahkan metode override biasa dan simpan skrip

 public override void Enter() { base.Enter(); SoundManager.Instance.PlaySound(SoundManager.Instance.jumpSounds); grounded = false; Jump(); } public override void LogicUpdate() { base.LogicUpdate(); if (grounded) { character.TriggerAnimation(landParam); SoundManager.Instance.PlaySound(SoundManager.Instance.landing); stateMachine.ChangeState(character.standing); } } public override void PhysicsUpdate() { base.PhysicsUpdate(); grounded = character.CheckCollisionOverlap(character.transform.position); } 

  • Di dalam Enter SoundManager singleton memainkan suara lompatan. Kemudian grounded reset ke nilai standarnya. Pada akhirnya, Jump dipanggil.
  • Di dalam PhysicsUpdate titik PhysicsUpdate sebelah kaki karakter dikirim ke CheckCollisionOverlap , yang berarti bahwa ketika karakter berada di tanah, grounded akan disetel ke true.
  • Di LogicUpdate , jika grounded benar, kami memanggil TriggerAnimation untuk mengaktifkan animasi touchdown, suara touchdown dimainkan, dan movementSM.CurrentState TriggerAnimation berubah menjadi character.standing .

Jadi, dalam hal ini kami telah menyelesaikan implementasi penuh perpindahan FSM menggunakan templat โ€œNegaraโ€ . Bangun proyek dan jalankan. Tekan Spasi untuk membuat karakter melompat.


Ke mana harus pergi selanjutnya?


Bahan - bahan proyek memiliki rancangan proyek dan proyek selesai.

Terlepas dari kegunaannya, mesin negara memiliki keterbatasan. Concurrent State Machines dan Pushdown Automaton machines dapat menangani beberapa batasan ini. Anda dapat membacanya di buku oleh Robert Nystrom Game Programming Patterns .

Selain itu, topik tersebut dapat dieksplorasi lebih dalam dengan memeriksa pohon perilaku yang digunakan untuk membuat entitas dalam game yang lebih kompleks.

Source: https://habr.com/ru/post/id484176/


All Articles