Advertisement
  1. Game Development
  2. Artificial Intelligence

Membuat AI Game Hoki Menggunakan Steering Behaviors: Game Mechanics

Scroll to top
Read Time: 12 min
This post is part of a series called Create AI for a Hockey Game Using Steering Behaviors.
Create a Hockey Game AI Using Steering Behaviors: Defense

() translation by (you can also view the original English article)

Dalam posting terakhir di seri ini, kami telah berfokus pada konsep di balik kecerdasan buatan yang telah kami pelajari. Pada bagian ini, kita akan menyelesaikan semua implementasi menjadi game hoki yang dapat dimainkan.  Anda akan belajar menambahkan potongan yang hilang yang diperlukan untuk mengubahnya menjadi sebuah game, seperti skor, power-up, dan sedikit desain game.

Hasil final

Berikut adalah game yang akan diimplementasikan dengan menggunakan semua elemen yang akan dijelaskan dalam tutorial ini.

Berpikir Dengan Disain Permainan

Bagian sebelumnya dari seri ini difokuskan untuk menjelaskan bagaimana permainan AI bekerja. Setiap bagian menjelaskan secara detail tentang aspek tertentu dari sebuah game, seperti bagaimana atlet bergerak dan bagaimana serangan dan pertahanan diterapkan.  Mereka didasarkan pada konsep seperti steering behaviors  dan stack-based finite state machines.

Untuk membuat permainan yang dapat dimainkan sepenuhnya, bagaimanapun, semua aspek itu harus dibungkus menjadi inti sebuah gamemekanik.  Pilihan yang paling jelas adalah menerapkan semua peraturan resmi pertandingan hoki yang sesungguhnya, tapi ini akan membutuhkan banyak kerja dan waktu. Mari kita mengambil pendekatan fantasi yang lebih sederhana.

Semua peraturan hoki akan diganti dengan yang tunggal: jika Anda membawa keping dan disentuh oleh lawan, maka Anda telah membekukan dan menghancurkan jutaan keping! Ini akan membuat permainan lebih mudah dimainkan dan menyenangkan bagi kedua pemain: yang membawa keping dan yang mencoba memulihkannya.

Untuk meningkatkan mekanik ini, kami akan menambahkan beberapa power-up. Mereka akan membantu pemain mencetak gol dan membuat permainan sedikit lebih dinamis.

Menambahkan Kemampuan untuk Skor

Mari kita mulai dengan sistem penilaian, yang bertanggung jawab untuk menentukan siapa yang menang atau kalah. Tim akan dinilai setiap kali puck (bola hoki) memasuki gawang lawan.

Cara termudah untuk menerapkannya adalah dengan menggunakan dua persegi panjang yang tumpang tindih:

Overlapped rectangles describing the goal area If the puck collides with the red rectangle the team scoresOverlapped rectangles describing the goal area If the puck collides with the red rectangle the team scoresOverlapped rectangles describing the goal area If the puck collides with the red rectangle the team scores
Persegi panjang yang tumpang tindih menggambarkan area sasaran. Jika puck berbenturan dengan persegi panjang merah, maka tim akan mendapat skor.

Persegi panjang hijau mewakili daerah yang ditempati oleh struktur gol (bingkai dan jaring). Ia bekerja seperti blok padat, sehingga puck dan atlit tidak akan bisa bergerak melewatinya; mereka akan terhempas kembali

Kotak merah mewakili "area skor". Jika puck menumpang tindih persegi panjang ini, itu berarti tim baru saja mencetak gol.

Kotak merah berukuran lebih kecil dari yang hijau, dan diletakkan di depannya, jadi jika puck menyentuh gawang di sisi manapun tapi di depan, bola akan kembali dan tidak ada skor yang akan ditambahkan:

A few examples of how the puck would behave if it touched the rectangles while movingA few examples of how the puck would behave if it touched the rectangles while movingA few examples of how the puck would behave if it touched the rectangles while moving
Beberapa contoh bagaimana kepingnya akan berperilaku jika menyentuh segi empat saat bergerak.

Mengorganisir Segalanya Sesudah Seseorang

Setelah tim di beri skor, semua atlet harus kembali ke posisi awal dan puck harus ditempatkan di pusat arena lagi. Setelah proses ini, pertandingan bisa dilanjutkan.

Memindahkan Atlet ke Posisi Awal Mereka

Seperti yang dijelaskan di bagian pertama seri ini, semua atlet memiliki kedudukan AI yang disebut prepareForMatch yang akan memindahkan mereka ke posisi awal, dan menyebabkannya berhenti secara perlahan di tempat tersebut.

Ketika puck tumpang tindih dengan salah satu "area skor", kedudukan aktif AI para atlet saat ini akan dibatalkan dan prepareForMatch akan otomatis terbentuk. Di mana pun para atlet berada, mereka akan kembali ke posisi awal mereka setelah beberapa detik:

Memindahkan Kamera Menuju Pusat Rink

Karena kamera selalu mengikuti kemanapun puck bergerak, maka jika kamera langsung berputar ke pusat arena setelah seseorang mendapat nilai, pandangan saat ini akan tiba-tiba berubah, pemandangan yang akan tertangkap pada saat itu menjadi jelek dan membingungkan.

Cara yang lebih baik untuk melakukan ini adalah dengan memindahkan puck dengan lancar menuju pusat arena; Karena kamera mengikuti puck-nya, sehingga kamera dapat dengan anggun akan meluncur dari gawang ke pusat arena.

Hal ini dapat dicapai dengan mengubah vektor kecepatan puck setelah menyentuh area sasaran. Varian kecepatan baru harus "mendorong" puck  ke arah pusat arena, sehingga dapat dihitung sebagai:

1
var c :Vector3D = getRinkCenter();
2
var p :Vector3D = puck.position;
3
4
var v :Vector3D = c - p;
5
v = normalize(v) * 100;
6
7
puck.velocity = v;

Dengan mengurangi posisi pusat arena dari posisi puck saat ini, sangatlah meungkinkan untuk menghitung vektor yang mengarah langsung ke pusat arena.

Setelah menormalisasi vektor ini, ini dapat diskalakan dengan nilai berapapun, misalnya 100, yang mengontrol seberapa cepat puck bergerak menuju pusat arena.

Berikut adalah gambar dengan representasi vektor kecepatan baru:

Calculation of a new velocity vector that will move the puck towards the rink centerCalculation of a new velocity vector that will move the puck towards the rink centerCalculation of a new velocity vector that will move the puck towards the rink center
Perhitungan vektor kecepatan baru yang akan menggerakkan puck ke arah arena center. 

Vektor V ini digunakan sebagai vektor kecepatan puck, sehingga puck-nya akan bergerak menuju pusat arena seperti yang diinginkan.

Untuk mencegah perilaku asing sketika puck bergerak menuju pusat arena, seperti interaksi dengan atlet, maka puck akan dinonaktifkan selama proses berlangsung.  Sebagai konsekuensinya, ia berhenti berinteraksi dengan atlit dan ditandai sebagai tidak terlihat. Pemain tidak akan melihat puck-nya bergerak, tapi kamera tetap akan mengikutinya.

Untuk menentukan apakah puck sudah berada dalam posisinya, jarak antara posisi tersebut dan pusat arena dihitung selama gerakan. Jika kurang dari 10, misalnya, puck cukup dekat untuk ditempatkan langsung di pusat arena dan diaktifkan kembali sehingga pertandingan bisa berlanjut.

Menambahkan Level Kekuatan (Power-Up)

Gagasan di balik menambah level kekuatan adalah membantu pemain mencapai tujuan utama permainan, yaitu mencetak gol dengan membawa puck ke gawang lawan.

Dikarenakan jangkauan, permainan kita hanya akan memiliki dua power-up: Ghost Help and Fear The Puck. Pada awalnya kita menambahkan tiga atlet tambahan ke tim pemain untuk beberapa waktu, namun untuk selanjutnya kita membuat lawan lari dari puck selama beberapa detik.

Power-up ditambahkan ke kedua tim saat ada yang mendapat nilai.

Menerapkan Power-up "Ghost Help"  

Karena semua atlit yang ditambahkan oleh power-up Ghost Help bersifat sementara, kelas Athlete harus dimodifikasi untuk memungkinkan atlit ditandai sebagai "ghost". Jika atlit adalah ghost, ia akan melepaskan diri dari permainan setelah beberapa detik. 

Di bawah ini adalah kelas Athlete, sorotilah penambahan yang dibuat untuk mengakomodasi fungsi ghost:

1
public class Athlete
2
{
3
    // (...)

4
  private var mGhost :Boolean;        // tells if the athlete is a ghost (a powerup that adds new athletes to help steal the puck).

5
	private var mGhostCounter :Number;  // counts the time a ghost will remain active

6
	
7
	public function Athlete(thePosX :Number, thePosY :Number, theTotalMass :Number) {
8
		// (...)

9
		mGhost = false;
10
		mGhostCounter = 0;
11
		
12
		// (...)

13
	}
14
    
15
    public function setGhost(theStatus :Boolean, theDuration :Number) :void {
16
		mGhost = theStatus;
17
		mGhostCounter = theDuration;
18
	}
19
	
20
	public function amIAGhost() :Boolean {
21
		return mGhost;
22
	}
23
	
24
	public function update() :void {
25
		// (...)

26
		
27
		// Update powerup counters and stuff

28
		updatePowerups();
29
		
30
		// (...)

31
	}
32
    
33
    public function updatePowerups() :void {
34
        // TODO.

35
    }
36
}

Properti mGhost adalah sebuah boolean yang memberitahu jika atlit adalah ghost atau tidak, sementara mGhostCounter berisi jumlah detik yang harus ditunggu atlet sebelum melepaskan dirinya dari permainan.

Kedua properti tersebut digunakan oleh metode updatePowerups ():

1
private function updatePowerups():void {		
2
	// If the athlete is a ghost, it has a counter that controls

3
	// when it must be removed.

4
	if (amIAGhost()) {
5
		mGhostCounter -= time_elapsed;
6
		
7
		if (mGhostCounter <= 2) {
8
			// Make athlete flicker when it is about to be removed.

9
			flicker(0.5);
10
		}
11
		
12
		if (mGhostCounter <= 0) {
13
			// Time to leave this world! (again)

14
			kill();
15
		}
16
	}
17
}

Metode updatePowerups (), yang disebut dalam rutinitas pemutakhiran atlet (), akan menangani semua pemrosesan power-up di atlet.  Sekarang semua yang dilakukannya adalah memeriksa apakah atlit saat ini adalah ghost atau tidak. Jika iya, maka properti mGhostCounter dikurangi dengan jumlah waktu yang telah berlalu sejak update terakhir.

Bila nilai mGhostCounter mencapai nol, berarti atlet temporer tersebut sudah cukup lama aktif, sehingga harus keluar dari permainan. Untuk membuat pemain sadar akan hal tersebut, atlet akan mulai berkedip dua detik terakhir sebelum menghilang.

Akhirnya, saatnya untuk menerapkan proses penambahan atlet temporer saat power-up diaktifkan. Ini dilakukan dalam metode powerupGhostHelp (), tersedia dalam logika game utama:

1
private function powerupGhostHelp() :void {
2
	var aAthlete :Athlete;	
3
	
4
	for (var i:int = 0; i < 3; i++) {
5
        // Add the new athlete to the list of athletes

6
		aAthlete = addAthlete(RINK_WIDTH / 2, RINK_HEIGHT - 100);
7
        
8
        // Mark the athlete as a ghost which will be removed after 10 seconds.

9
		aAthlete.setGhost(true, 10);
10
	}
11
}

Metode ini dilakukan berulangkali lebih dari satu putaran yang sesuai dengan jumlah atlet temporer yang ditambahkan. Setiap atlit baru ditambahkan ke bawah arena dan ditandai sebagai ghost.

Seperti yang dijelaskan sebelumnya, atlet hantu akan melepaskan diri dari permainan.

Menerapkan Power-Up "Fear The Puck" 

Power-up Fear The Puck membuat semua lawan melarikan diri dari puck selama beberapa detik.

Sama seperti power-up Ghost Help, kelas Athlete harus dimodifikasi untuk mengakomodasi fungsi itu: 

1
public class Athlete
2
{
3
    // (...)

4
    private var mFearCounter :Number; // counts the time the athlete should evade from puck (when fear powerup is active).

5
6
	
7
	public function Athlete(thePosX :Number, thePosY :Number, theTotalMass :Number) {
8
		// (...)

9
		mFearCounter = 0;
10
		
11
		// (...)

12
	}
13
    
14
	public function fearPuck(theDuration: Number = 2) :void {
15
		mFearCounter = theDuration;
16
	}
17
	
18
    // Returns true if the mFearCounter has a value and the athlete

19
    // is not idle or preparing for a match.

20
	private function shouldIEvadeFromPuck() :Boolean {
21
		return mFearCounter > 0 && mBrain.getCurrentState() != idle && mBrain.getCurrentState() != prepareForMatch;
22
	}
23
    
24
    private function updatePowerups():void {
25
    	if(mFearCounter > 0) {
26
			mFearCounter -= elapsed_time;
27
		}
28
		
29
		// (...)

30
	}
31
	
32
	public function update() :void {
33
		// (...)

34
		
35
		// Update powerup counters and stuff

36
		updatePowerups();
37
        
38
		// If the athlete is an AI-controlled opponent

39
		if (amIAnAiControlledOpponent()) {
40
			// Check if "fear of the puck" power-up is active.

41
            // If that's true, evade from puck.

42
			if(shouldIEvadeFromPuck()) {
43
				evadeFromPuck();
44
			}
45
        }			
46
		
47
		// (...)

48
	}
49
    
50
    public function evadeFromPuck() :void {
51
        // TODO

52
    }
53
}

Pertama, metode updatePowerups () diubah menjadi penurunan properti mFearCounter, yang berisi jumlah waktu atlet harus untuk menghindar dari puck-nya. Properti mFearCounter diubah setiap kali metode fearPuck () dipanggil.

Dalam metode update Athlete (), sebuah tes ditambahkan untuk memeriksa apakah power-up harus dilakukan. Jika atlit adalah lawan yang dikendalikan oleh AI (amIAnAiControlledOpponent() returns true) dan atlet harus menghindari puck (shouldievadeFromPuck() juga mengembalikan nilai true), metode evadeFromPuck () akan dimunculkan. 

Metode evadeFromPuck () menggunakan  evade behavior, yang membuat entitas menghindari objek dan lintasannya:

1
private function evadeFromPuck() :void {
2
	mBoid.steering = mBoid.steering + mBoid.evade(getPuck().getBoid());
3
}

Semua metode evadeFromPuck () menambahkan kekuatan penghindar ke kekuatan kemudi atlet saat ini. Ini membuat dia menghindari puck tanpa mengabaikan kekuatan kemudi yang sudah ditambahkan, seperti yang diciptakan oleh negara AI yang aktif saat ini.

Agar bisa dihindari, keping harus berperilaku seperti boid, seperti yang semua atlet lakukan (informasi lebih lengkap ada di bagian pertama seri ini). Sebagai konsekuensinya, properti boid, yang berisi posisi dan kecepatan puck saat ini, harus ditambahkan ke kelas Puck:

1
class Puck {
2
    // (...)

3
    private var mBoid :Boid;
4
    
5
    // (...)

6
    
7
    public function update() {
8
        // (...)

9
        mBoid.update();
10
    }
11
    
12
    public function getBoid() :Boid {
13
        return mBoid;
14
    }
15
    
16
    // (...)

17
}

Akhirnya, kami memperbarui logika permainan utama untuk membuat lawan takut akan puck saat power-up diaktifkan:

1
private function powerupFearPuck() :void {
2
	var i           :uint,
3
		athletes    :Array  = rightTeam.members,
4
		size        :uint   = athletes.length;
5
			
6
	for (i = 0; i < size; i++) {
7
		if (athletes[i] != null) {
8
            // Make athlete fear the puck for 3 seconds.

9
			athletes[i].fearPuck(3);
10
		}
11
	}
12
}

Metode ini dilakukan berulang atas semua atlet lawan (tim kanan, dalam kasus ini), memanggil metode fearkPuck() pada setiap dari mereka. Hal ini akan memicu logika yang membuat atlet menjauhi puck dalam beberapa detik, seperti yang dijelaskan sebelumnya.

Pembekuan dan penghancuran

Penambahan permainan pada permainan adalah bagian pembekuan dan penghancuran. Hal itu dilakukan dalam logika permainan utama, di mana pemeriksaan rutin apakah atlet tim kiri tumpang tindih dengan atlit tim kanan.

Pemeriksaan tumpang tindih ini secara otomatis dilakukan oleh mesin permainan Flixel, yang memanggil setiap kali tumpang tindih ditemukan:

1
private function athletesOverlapped(theLeftAthlete :Athlete, theRightAthlete :Athlete) :void {
2
    // Does the puck have an owner?	

3
	if (mPuck.owner != null) {
4
        // Yes, it does.

5
		if (mPuck.owner == theLeftAthlete) {
6
            //Puck's owner is the left athlete

7
			theLeftAthlete.shatter();
8
			mPuck.setOwner(theRightAthlete);
9
10
		} else if (mPuck.owner == theRightAthlete) {
11
            //Puck's owner is the right athlete

12
			theRightAthlete.shatter();
13
			mPuck.setOwner(theLeftAthlete);
14
		}
15
	}
16
}

Pemanggilan ini diterima sebagai parameter para atlet dari masing-masing tim yang tumpang tindih. Sebuah tes yang dilakukan umtuk memerikas apakah pemilik puck itu tidak dibatalkan, yang berarti sedang dibawa oleh seseorang.

Dalam hal ini, pemilik puck itu dibandingkan dengan atlit yang tumpang tindih. Jika salah satu dari mereka membawa puck (jadi dia adalah pemilik puck), maka atlit tersebut hancur dan kepemilikan puck beralih pada atlet lainnya.

Metode shatter() di kelas Athlete akan menandai ketidak aktifan atlit dan menempatkannya di dasar arena setelah beberapa detik. Ini juga akan memancarkan beberapa partikel yang merepresentasikan potongan es, tapi topik ini akan dibahas di artikel lain.

Kesimpulan 

Dalam tutorial ini, kami menerapkan beberapa elemen yang diperlukan untuk mengubah prototipe hoki menjadi permainan yang dapat dimainkan sepenuhnya. Saya sengaja menempatkan fokus pada konsep di balik masing-masing elemen tersebut, daripada bagaiman sebenarnya menerapkannya di mesin game X atau Y.

Pendekatan pembekuan dan penghancuran yang digunakan untuk game mungkin terdengar terlalu fantastis, namun membantu agar proyek tetap dikelola. Aturan olahraga sangat spesifik, dan implementasinya bisa rumit.

Dengan menambahkan beberapa layar dan beberapa elemen HUD, Anda dapat membuat permainan hoki Anda sendiri dari demo ini!

Referensi

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.