() translation by (you can also view the original English article)
"Tic-Tac-Toe, itu membosankan!" Anda mungkin berpikir. Dalam tutorial ini saya akan menunjukkan kepada Anda bahwa membangun game Tic-Tac-Toe sama sekali tidak membosankan. Ini adalah game yang ideal untuk dibuat ketika Anda ingin belajar ActionScript 3.0. Sementara membangunnya, Anda akan belajar banyak tentang fungsi dan event listener, Anda akan melihat betapa mudahnya untuk menyesuaikan grafik permainan dan Anda bahkan akan belajar cara memprogram Artificial Intelligence (AI)!
Pratinjau Hasil Akhir
Mari kita lihat hasil akhir yang akan kita kerjakan:
Langkah 1: Persiapan
Kami akan mulai dengan membuat file Flash baru (File > New > Flash File (ActionScript 3.0)). Atur dimensi stage ke 300x300 dan gunakan latar belakang hitam. Di properti (Window> Properties), isi "TicTacToe" sebagai definisi kelas. Sekarang simpan file di folder proyek Anda (mis. Desktop\MyFirstGame\) sebagai "TicTacToe.fla".
Selanjutnya, kita akan mengatur file AS3 yang akan kita gunakan. Kami mulai dengan tiga file .as, yang juga kami simpan di folder proyek kami. Tulis kode berikut dan simpan file dengan nama yang sama dengan nama kelas. Jadi kelas TicTacToe yang Anda simpan sebagai "TicTacToe.as", dll.
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.MovieClip; |
5 |
|
6 |
public class TicTacToe extends MovieClip |
7 |
{
|
8 |
public function TicTacToe():void |
9 |
{
|
10 |
trace("TicTacToe"); |
11 |
}
|
12 |
}
|
13 |
}
|
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.MovieClip; |
5 |
|
6 |
public class Game extends MovieClip |
7 |
{
|
8 |
public function Game(num:uint):void |
9 |
{
|
10 |
trace("Game"); |
11 |
}
|
12 |
}
|
13 |
}
|
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.MovieClip; |
5 |
|
6 |
public class Tile extends MovieClip |
7 |
{
|
8 |
public function Tile():void |
9 |
{
|
10 |
trace("Tile"); |
11 |
}
|
12 |
}
|
13 |
}
|
Kelas pertama, TicTacToe.as, akan menjadi kelas dokumen kami. Tidak yakin apa maksudnya? Baca pengantar cepat ini untuk kelas dokumen.
Langkah 2: Latar Belakang Yang Sedikit Lebih Banyak
Kami sekarang memiliki empat file di folder proyek kami: satu file .fla dan tiga file .as. Ketika Anda menguji Flash movie Anda (Control > Test Movie), Anda akan melihat stage dengan latar belakang hitam dan "TicTacToe" di layar Output (Window > Output). Bagus, tapi apa yang akan kita lakukan dengannya?
File TicTacToe.as akan menangani fungsionalitas menu kami, yang akan kami tulis di akhir tutorial ini (lebih menyenangkan untuk mendapatkan game yang berfungsi secepat mungkin, kan?). Menu hanya akan memiliki dua tombol, satu pemain atau dua pemain.
File Game.as akan menangani game kami. Ini juga mengapa kami meminta argumen "num"; ini akan menentukan jumlah pemain manusia. kami akan mulai dengan 2, tetapi nanti kami juga ingin menulis fungsionalitas ketika hanya ada satu pemain. Dalam hal ini, komputer harus bermain sebagai pemain dua.
File terakhir, Tile.as, akan kami gunakan untuk membuat ubin (Anda dapat menebaknya). Maksud saya, ubin adalah salah satu dari sembilan tempat di mana Anda dapat menempatkan O atau X (atau gambar bunga, hewan peliharaan Anda, logo, sebut saja).
Langkah 3: Grafik Garis Grid
Kami akan mulai dengan membuat garis. Anda bisa menjadi sekreatif yang Anda inginkan, tetapi pastikan garis-garisnya membagi stage menjadi 9 blok yang sama. Inilah yang saya lakukan: Saya membuat MovieClip baru (Insert > New Symbol) dan menggambar 2 garis horizontal dan 2 garis vertikal dengan panjang 300px. Garis horizontal yang saya atur di x = 0 dan (y = 100 || y = 200). Garis vertikal saya atur di (x = 100 || x = 200) dan y = 0.



Di perpustakaan Anda (Window > Library), klik kanan MovieClip baru Anda, pergi ke Properties dan centang kotak yang mengatakan "Export for ActionScript". Isi dengan "Grrid" sebagai definisi kelas.
Langkah 4: Ubin Grafik
Selanjutnya kita akan membuat ubin O dan X. Anda dapat menggunakan gambar apa pun yang Anda inginkan, meskipun Anda harus mengingat satu hal: grid kami membagi stage kami menjadi 9 ubin persegi 100x100px, sehingga setiap ubin tidak boleh lebih besar dari 100x100. Inilah yang saya lakukan: Saya membuat MovieClip baru dan membuat bentuk dengan Rectangle Tool 100x100. Bentuk ini saya selaraskan sehingga bagian kiri atas adalah titik registrasi MovieClip. Warna isian bisa apa saja, tetapi atur ke alpha 0%. Dengan cara ini ubin MovieClip Anda selalu 100x100 dan Anda dapat menggambar gambar apa pun yang Anda inginkan di atas bentuk 100x100.



Anda dapat menghapus latar belakang 100x100 setelah Anda menggambar bentuk X dan O, tetapi itu tidak terlalu menjadi masalah. Ini adalah game kecil sehingga tidak perlu menghapus bentuk dengan mempertimbangkan kinerja. Saya merasa mudah untuk meninggalkannya sehingga saya dapat mengubah grafik di kemudian hari tanpa kehilangan orientasi di mana saya dapat meletakkan semuanya. Anda hanya perlu memilih kedua grafik (bentuk 100x100 dan grafik Anda sendiri) dan menyelaraskannya ke tengah (pastikan Align To Stage tidak aktif), dan Anda selesai.
Ini adalah dua MovieClips yang saya buat. Dua telur kecil di sebelah kanan adalah hasil akhir. Saya tidak akan masuk ke detail tentang cara membuat angka telur keren, tutorial ini adalah tentang membuat game. Percobaan dengan grafik Anda sendiri, dan coba hal-hal baru seperti menggunakan timeline MovieClip Anda.



Ekspor kedua MovieClips untuk ActionScript dengan cara yang sama seperti yang Anda lakukan dengan Grid. Pastikan Anda memberikan nama definisi kelas TileO dan TileX. Kami sekarang memiliki tiga item di perpustakaan kami: Grid, TileO dan TileX. Kita hanya perlu dua lagi sebelum kita bisa membuat skrip!
Langkah 5: Penyorot Grafis
Seperti yang saya katakan, kita hampir siap untuk melakukan beberapa scripting, tetapi kita perlu dua grafik lagi. Ketika seseorang memenangkan game, kami tidak ingin memulai game baru segera tetapi melihat garis game terlebih dahulu. Untuk ini kita memerlukan bentuk untuk posisi di atas ubin ketika ubin adalah bagian dari garis yang menang. Saya hanya ,membuat bentuk 100x100 putih dengan alpha diatur ke 50%, tapi saya tahu Anda bisa melakukan yang lebih baik. Anda dapat membuat tanda V hijau yang bagus, senyum bahagia, apa saja. Ekspor MovieClip untuk ActionScript dengan TileE definisi kelas (E untuk "END"; Saya memiliki sesuatu tentang nama pendek, dan dalam game sederhana seperti ini Anda akan ingat apa artinya).
Grafik terakhir yang perlu Anda buat adalah tombol ": ubin, tempat kami menerapkan semua event listener dalam ubin. Sekali lagi, buat bentuk 100x100 dengan alpha yang disetel ke 0%. Pastikan, seperti semua grafik ubin yang kami buat, bahwa titik pendaftaran diatur ke sudut kiri atas. Ekspor MovieClip untuk ActionScript dengan TileB definisi kelas (B untuk "BUTTON").
Kami sekarang memiliki lima MovieClips di perpustakaan kami yang akan kami gunakan untuk membangun game Tic-Tac-Toe.



Langkah 6: Game Baru
Kami akan mulai dengan lambat. Buka kelas dokumen Anda (TicTacToe.as) dan terapkan perubahan berikut:
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.MovieClip; |
5 |
|
6 |
public class TicTacToe extends MovieClip |
7 |
{
|
8 |
public function TicTacToe():void |
9 |
{
|
10 |
var game:Game = new Game(2); // Add a new game |
11 |
addChild(game); // Add the game to the display list |
12 |
}
|
13 |
}
|
14 |
}
|
Itu saja. Ketika kami sekarang menguji Flash Movie, jendela Output akan menampilkan Game. Kami menyediakan 2 sebagai argumen untuk konstruktor Game() karena kami ingin memulai dengan membangun game untuk dua pemain. Kami juga menambahkan game ke daftar tampilan, tetapi tidak akan melihat apa-apa karena kelas game hanya berisi pernyataan jejak saat ini.
Langkah 7: Ubin!
Akan menyenangkan jika ini adalah langkah 9, bukan?
...
Bagaimanapun! Kami akan menambahkan ubin ke game. Saya akan menambahkan banyak kode sekaligus, dan hasilnya adalah kita tidak akan melihat apa pun kecuali 9 jejak dengan kata "Tile", tapi itu sepadan. Saya akan menjelaskan semua kode, tetapi cobalah membaca dan memahaminya sendiri terlebih dahulu.
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.MovieClip; |
5 |
import flash.events.Event; |
6 |
|
7 |
public class Game extends MovieClip |
8 |
{
|
9 |
public var numPlayers:uint; |
10 |
public var turn:uint; |
11 |
private var _grid:Grid = new Grid(); |
12 |
private var _tiles:Array = new Array(); |
13 |
|
14 |
public function Game(num:uint):void |
15 |
{
|
16 |
numPlayers = num; |
17 |
turn = 0; |
18 |
addVisuals(); |
19 |
addTileListeners(); |
20 |
nextTurn(); |
21 |
}
|
22 |
|
23 |
private function addVisuals():void |
24 |
{
|
25 |
var tile1:Tile = new Tile(); |
26 |
var tile2:Tile = new Tile(); |
27 |
var tile3:Tile = new Tile(); |
28 |
var tile4:Tile = new Tile(); |
29 |
var tile5:Tile = new Tile(); |
30 |
var tile6:Tile = new Tile(); |
31 |
var tile7:Tile = new Tile(); |
32 |
var tile8:Tile = new Tile(); |
33 |
var tile9:Tile = new Tile(); |
34 |
tile1.x = tile4.x = tile7.x = tile1.y = tile2.y = tile3.y = 0; |
35 |
tile2.x = tile5.x = tile8.x = tile4.y = tile5.y = tile6.y = 100; |
36 |
tile3.x = tile6.x = tile9.x = tile7.y = tile8.y = tile9.y = 200; |
37 |
addChild(tile1); |
38 |
addChild(tile2); |
39 |
addChild(tile3); |
40 |
addChild(tile4); |
41 |
addChild(tile5); |
42 |
addChild(tile6); |
43 |
addChild(tile7); |
44 |
addChild(tile8); |
45 |
addChild(tile9); |
46 |
addChild(_grid); |
47 |
_tiles.push(tile1, tile2, tile3, tile4, tile5, tile6, tile7, tile8, tile9); |
48 |
}
|
49 |
|
50 |
private function addTileListeners():void |
51 |
{
|
52 |
for (var i:int = 0; i < _tiles.length; i++) { |
53 |
_tiles[i].addEventListener("TILE_ACTIVE", nextTurn); |
54 |
}
|
55 |
}
|
56 |
|
57 |
public function nextTurn(e:Event = null):void |
58 |
{
|
59 |
trace("Next turn!"); |
60 |
turn += 1; |
61 |
}
|
62 |
}
|
63 |
}
|
Seperti yang Anda lihat, pertama saya membuat beberapa variabel. Variabel numPlayers melacak berapa banyak pemain yang bermain. Kita membutuhkan ini nanti ketika kita menambahkan AI. Hal yang sama berlaku untuk variabel turn, yang mencatat giliran siapa. Kami juga membuat _grid, sebuah instance dari simbol Grid, yang ingin kami tampilkan. Akhirnya, array _tiles akan melacak semua ubin.
Fungsinya adalah teman Anda, jadi saya menulis fungsi baru untuk menambahkan ubin, addVisuals() (Anda dapat menulis semua kode di konstruktor, tetapi ini lebih rapi). Dalam fungsi ini, pertama-tama kita membuat sembilan ubin baru. Posisi x dan y ubin diatur sedemikian rupa sehingga ubin akan diposisikan sebagai keypad ponsel:
1 |
|
2 |
x = 0 x = 100 x = 200 |
3 |
|
4 |
y = 0 tile1 tile2 tile3 |
5 |
y = 100 tile4 tile5 tile6 |
6 |
y = 200 tile7 tile8 tile9 |
Kami menambahkan setiap ubin ke daftar tampilan (yang tidak akan kami lihat, karena ubin belum memiliki visual), dan juga mendorongnya ke daftar _tiles (dalam urutan yang benar, ini sangat penting untuk pemeriksaan AI/menang) kemudian). Panggilan addChild() terakhir adalah untuk grid, yang ingin kami posisikan di atas semua ubin. Terakhir tapi bukan yang akhir, kami menambahkan event listener ke semua ubin dengan fungsi addTileListeners(). Event yang kami dengarkan belum ada, tetapi kami akan mengirimkannya dari kelas ubin nanti. Ketika event dipicu, ia menjalankan fungsi nextTurn yang akan menangani giliran dan memeriksa garis kemenangan. Tentu saja kita memanggil fungsi nextTurn() di dalam konstruktor sehingga kita dapat memulai game.
Langkah 8: AI Permulaan
Dimungkinkan untuk membuat sistem AI yang sangat kompleks, tetapi karena Tic-Tac-Toe adalah game yang agak kecil dengan opsi terbatas, lebih baik mengambil jalan keluar yang mudah. Kami menambahkan semua ubin ke Array _tiles. Ubin pertama (tile1) ditambahkan pertama, sehingga memiliki index = 0. Ubin kedua memiliki index = 1, dll. Jika kita memvisualisasikan ini di layar, akan terlihat seperti ini:
1 |
|
2 |
0 1 2 |
3 |
3 4 5 |
4 |
6 7 8 |
Inilah cara kami akan memeriksa apakah seseorang memiliki tiga berturut-turut: kami membuat array dari semua kombinasi yang memungkinkan Anda dapat menang. Sebagai contoh, 0 1 2 adalah kombinasi pemenang, dan begitu juga 1 4 7 dan 2 4 6(diagonal). Tambahkan variabel berikut ke skrip Game.as Anda:
1 |
|
2 |
private var _combos:Array = new Array(new Array(0, 1, 2), new Array(3, 4, 5), new Array(6, 7, 8), |
3 |
new Array(0, 3, 6), new Array(1, 4, 7), new Array(2, 5, 8), |
4 |
new Array(0, 4, 8), new Array(2, 4, 6)); |
Kemudian, kami akan memeriksa index semua ubin O dan X di dalam array _tiles. Ketika index, misalnya, empat ubin X adalah 0 4 5 dan 8, kita dapat memeriksa apakah kombinasi yang menang cocok dengan index ini. Misalnya, ada 0, tetapi tidak 1 dan 2, jadi ubin X tidak cocok dengan kombinasi pemenang 0 1 2. Kami akan melakukan ini untuk setiap kombinasi, dan pada akhirnya akan melihat bahwa kombinasi 0 4 8 telah dibuat. Script untuk ini akan diberikan beberapa langkah kemudian.
Langkah 9: .. Ubin di Layar!
Lagipula harus membuat lelucon. Saya mungkin membuat Anda takut dengan kode berikut untuk Tile.as, tetapi saya akan bertanya sekali lagi: coba cari tahu sendiri dan setelah membaca komentar saya pada kode. Ini mungkin tampak banyak, tetapi jika Anda membacanya dan membaca komentar Anda akan memahami cara kerjanya.
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.MovieClip; |
5 |
import flash.events.Event; |
6 |
import flash.events.EventDispatcher; |
7 |
import flash.events.MouseEvent; |
8 |
|
9 |
public class Tile extends MovieClip |
10 |
{
|
11 |
public var isSet:String; // Variable to hold which player clicked this tile |
12 |
|
13 |
private var tileB:TileB = new TileB(); |
14 |
private var tileE:TileE = new TileE(); |
15 |
private var tileO:TileO = new TileO(); |
16 |
private var tileX:TileX = new TileX(); |
17 |
|
18 |
public function Tile():void |
19 |
{
|
20 |
isSet = "not set"; // The tile is still free for players to use |
21 |
tileO.visible = false; // Set the tile visibility to false |
22 |
tileX.visible = false; |
23 |
tileE.visible = false; |
24 |
addChild(tileO); // Add the graphics in this order! |
25 |
addChild(tileX); |
26 |
addChild(tileE); |
27 |
addChild(tileB); // Button needs to be on top |
28 |
addButtonListeners(); // Function to add the listeners |
29 |
}
|
30 |
|
31 |
public function addButtonListeners():void // Public, as we want to use it from the game class later on |
32 |
{
|
33 |
this.buttonMode = true; |
34 |
tileB.addEventListener(MouseEvent.MOUSE_OVER, showGraphic); |
35 |
tileB.addEventListener(MouseEvent.MOUSE_OUT, hideGraphic); |
36 |
tileB.addEventListener(MouseEvent.CLICK, chooseGraphic); |
37 |
}
|
38 |
|
39 |
public function removeButtonListeners():void // Public, as we want to use it from the game class later on |
40 |
{
|
41 |
this.buttonMode = false; |
42 |
tileB.removeEventListener(MouseEvent.MOUSE_OVER, showGraphic); |
43 |
tileB.removeEventListener(MouseEvent.MOUSE_OUT, hideGraphic); |
44 |
tileB.removeEventListener(MouseEvent.CLICK, chooseGraphic); |
45 |
}
|
46 |
|
47 |
private function showGraphic(e:MouseEvent):void |
48 |
{
|
49 |
if (Game(this.parent).turn % 2 == 0) { // turn % 2 is always either 0 or 1: 0%2 = 0, 1%2 = 1, 2%2 = 0, 3%2 = 1, etc. |
50 |
tileO.visible = true; // It's O's turn, show the O graphic |
51 |
} else { |
52 |
tileX.visible = true; // It's X's turn, show the X graphic |
53 |
}
|
54 |
}
|
55 |
|
56 |
private function hideGraphic(e:MouseEvent):void |
57 |
{
|
58 |
tileO.visible = false; // Hide the graphics |
59 |
tileX.visible = false; // For both O and X |
60 |
}
|
61 |
|
62 |
private function chooseGraphic(e:MouseEvent):void |
63 |
{
|
64 |
removeButtonListeners(); // Remove the listeners (this tile is now disabled) |
65 |
isSet = (Game(this.parent).turn % 2 == 0) ? "O" : "X"; // Set the isSet variable to O or X |
66 |
trace(isSet); |
67 |
dispatchEvent(new Event("TILE_ACTIVE")); // Dispatch the event |
68 |
}
|
69 |
}
|
70 |
}
|
Apakah Anda sudah mengujinya? Karena jika semuanya berjalan dengan baik, Anda akan melihat game yang berfungsi sekarang! Mari kita bahas skripnya sebentar.
Kami mulai dengan membuat semua gambar. Masing-masing dari sembilan ubin memiliki grafis TileB, TileE, TileX dan TileO. Di konstruktor (fungsi publik Tile()) kami menambahkan grafik ini ke daftar tampilan dan mengatur visibilitas ke false. Hanya grafik TileB yang tetap pada visible = true, karena kami ingin menambahkan event listener kami ke grafik ini. Tidak masalah; itu sebabnya kami mengatur alpha ke 0%.
Baik fungsi addButtonListeners() dan removeButtonListeners() berbicara sendiri. Saat kami menempatkan mouse di atas ubin, kami ingin grafik yang benar ditampilkan. Saat kami memindahkan mouse dari ubin, kami ingin menyembunyikan gambar. Ketika kami mengklik, kami ingin itu menunjukkan grafik dan membuatnya tetap terlihat sehingga pemain berikutnya dapat bermain. Kami melakukan ini dengan operator modulo.
Dalam komputasi, operasi modulo menemukan sisa pembagian satu angka dengan yang lain - Wikipedia.
Saat kami mengeklik ubin, fungsi chooseGraphic(e:MouseEvent) menjadi pemicu. Dalam fungsi ini kami menghapus semua lisener, mengatur variabel isSet (melacaknya) dan menjalankan event TILE_ACTIVE. Langkah selanjutnya: periksa siapa yang menang!
Langkah 10: Game Bagus!
Mari kita mulai dengan mudah: tambahkan skrip berikut ke file Tile.as untuk menangani situasi kemenangan:
1 |
|
2 |
public function showEnd():void { // Added to show the tileE |
3 |
tileE.visible = true; |
4 |
}
|
Mudah bukan? Ini adalah fungsi publik sehingga kami dapat memanggilnya dari kelas Game, dan yang dilakukannya hanyalah mengatur visibilitas tileE menjadi true. Sekarang buka Game.as lagi dan bersiaplah untuk beberapa skrip serius. Kami akan menambahkan satu variabel, mengedit fungsi nextTurn() dan menambahkan dua fungsi baru:
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.MovieClip; |
5 |
import flash.events.Event; |
6 |
|
7 |
public class Game extends MovieClip |
8 |
{
|
9 |
public var numPlayers:uint; |
10 |
public var turn:uint; |
11 |
public var left:uint = 10; // Added to keep track of free tiles; 10 because we do -1 once without marking a tile |
12 |
private var _grid:Grid = new Grid(); |
13 |
private var _tiles:Array = new Array(); |
14 |
private var _combos:Array = new Array(new Array(0, 1, 2), new Array(3, 4, 5), new Array(6, 7, 8), |
15 |
new Array(0, 3, 6), new Array(1, 4, 7), new Array(2, 5, 8), |
16 |
new Array(0, 4, 8), new Array(2, 4, 6)); |
17 |
|
18 |
public function Game(num:uint):void |
19 |
{
|
20 |
numPlayers = num; |
21 |
turn = 0; |
22 |
addVisuals(); |
23 |
addTileListeners(); |
24 |
nextTurn(); |
25 |
}
|
26 |
|
27 |
private function addVisuals():void |
28 |
{
|
29 |
var tile1:Tile = new Tile(); |
30 |
var tile2:Tile = new Tile(); |
31 |
var tile3:Tile = new Tile(); |
32 |
var tile4:Tile = new Tile(); |
33 |
var tile5:Tile = new Tile(); |
34 |
var tile6:Tile = new Tile(); |
35 |
var tile7:Tile = new Tile(); |
36 |
var tile8:Tile = new Tile(); |
37 |
var tile9:Tile = new Tile(); |
38 |
tile1.x = tile4.x = tile7.x = tile1.y = tile2.y = tile3.y = 0; |
39 |
tile2.x = tile5.x = tile8.x = tile4.y = tile5.y = tile6.y = 100; |
40 |
tile3.x = tile6.x = tile9.x = tile7.y = tile8.y = tile9.y = 200; |
41 |
addChild(tile1); |
42 |
addChild(tile2); |
43 |
addChild(tile3); |
44 |
addChild(tile4); |
45 |
addChild(tile5); |
46 |
addChild(tile6); |
47 |
addChild(tile7); |
48 |
addChild(tile8); |
49 |
addChild(tile9); |
50 |
addChild(_grid); |
51 |
_tiles.push(tile1, tile2, tile3, tile4, tile5, tile6, tile7, tile8, tile9); |
52 |
}
|
53 |
|
54 |
private function addTileListeners():void |
55 |
{
|
56 |
for (var i:int = 0; i < _tiles.length; i++) { |
57 |
_tiles[i].addEventListener("TILE_ACTIVE", nextTurn); |
58 |
}
|
59 |
}
|
60 |
|
61 |
public function nextTurn(e:Event = null):void |
62 |
{
|
63 |
if (!checkWin()) { // Only if the checkWin function returns false, continue |
64 |
left -= 1; // Someone chose a tile so there's one less left to choose from |
65 |
if (left > 0) { // While there are tiles left... |
66 |
turn += 1; // Add one to the turn (next player is up) |
67 |
} else { |
68 |
endGame(); // Else end the game (it's a draw) |
69 |
}
|
70 |
}
|
71 |
}
|
72 |
|
73 |
private function checkWin():Boolean |
74 |
{
|
75 |
var won:Boolean = false; // First set a boolean to false |
76 |
for each (var combi:Array in _combos) { // For each combi (array) in our _combos array |
77 |
var xwin = true; // Assume x won |
78 |
var owin = true; // Assume o won |
79 |
for each (var tile:int in combi) { // For each tile index (number) in our combi array |
80 |
xwin = xwin && _tiles[tile].isSet == "X"; // Set xwin and owin to true, but only when there is an X or O |
81 |
owin = owin && _tiles[tile].isSet == "O"; // with the same index as the number in the combi array |
82 |
}
|
83 |
if (xwin || owin) { // If either xwin or owin is true |
84 |
endGame(combi); // End the game (supply the combi) |
85 |
won = true; // Set won to true |
86 |
break; // Break out of the loop (important! there is a winning combination so no more checking needed) |
87 |
} else { |
88 |
won = false; // Else set won to false (no combination yet) |
89 |
}
|
90 |
}
|
91 |
return won; // Return won |
92 |
}
|
93 |
|
94 |
private function endGame(combi:Array = null):void |
95 |
{
|
96 |
for (var i:int = 0; i < _tiles.length; i++) { // For each tile in the tiles Array |
97 |
_tiles[i].removeButtonListeners(); // Remove the listeners |
98 |
if (combi) { // If there is a combi supplied (someone won) |
99 |
for each (var j:int in combi) { // Check each number in that combi... |
100 |
if (i == j) _tiles[i].showEnd(); // And highlight the corresponding tile with showEnd() |
101 |
}
|
102 |
}
|
103 |
}
|
104 |
}
|
105 |
}
|
106 |
}
|
Kita mulai dengan variabel left. Ketika ini mencapai nol, itu berarti tidak ada ubin lagi yang tersisa dan game berakhir. Biasanya ini berarti seri.
Selanjutnya adalah fungsi nextTurn(e: Event = null), yang pertama hanya turn += 1 di dalamnya. Seperti yang Anda lihat, sekarang memiliki lebih banyak kondisi. Setiap kali fungsi dipanggil, kami memeriksa apakah seseorang menang. Jika tidak, kami tahu ada satu petak yang lebih sedikit untuk dipilih, jadi kami lakukan left -= 1. Selama ada ubin yang tersisa, kami dapat membiarkan pemain lain melakukan langkahnya. Tidak ada ubin yang tersisa berarti akhir game (seri, karena kami hanya mencapai bagian skrip ini jika belum ada situasi kemenangan).
Fungsi checkWin() paling baik dijelaskan dengan sebuah contoh. Mari kita ambil situasi berikut:
1 |
|
2 |
tile1 = X tile 4 = O |
3 |
tile2 = X tile 5 = O |
4 |
tile3 = X |
Tile1 memiliki index 0 di dalam array _tiles. Yang pertama untuk pernyataan for each memeriksa semua Array yang bersarang di dalam array _combos. Yang pertama berisi "0 1 2". Yang pernyataan each kedua menangani angka-angka ini (yang mewakili index ubin). Inilah yang terjadi pada pernyataan for each kedua:
1 |
|
2 |
first combo = 0 1 2 |
3 |
|
4 |
- FIRST PASS _tiles[0].isSet = X |
5 |
var tile = 0 |
6 |
xwin (true) = xwin (true) && _tiles[0].isSet == X (true) |
7 |
owin (flase) = owin (true) && _tiles[0].isSet == O (false) |
8 |
|
9 |
- SECOND PASS _tiles[1].isSet = X |
10 |
var tile = 1 |
11 |
xwin (true) = xwin (true) && _tiles[1].isSet == X (true) |
12 |
owin (flase) = owin (false) && _tiles[1].isSet == O (false) |
13 |
|
14 |
- THIRD PASS _tiles[2].isSet = X |
15 |
var tile = 2 |
16 |
xwin (true) = xwin (true) && _tiles[2].isSet == X (true) |
17 |
owin (flase) = owin (false) && _tiles[2].isSet == O (false) |
Seperti yang Anda lihat, ketika pernyataan for each kedua selesai, xwin akan true dan owin akan false. Ini memicu pernyataan if dan panggilan untuk fungsi endGame(combi: Array = null). Dengan itu, ia memasukan kombinasi pemenang yang akan kami gunakan dalam fungsi. Fungsi checkWin() mengembalikan true atau false, yang saya jelaskan sebelumnya.
Fungsi endGame() mudah dimengerti. Pertama, untuk setiap ubin di Array _tiles, kami menonaktifkan event listener. Kami juga memeriksa apakah ada kombinasi yang disediakan (artinya seseorang menang bukannya seri). Jika demikian, kami memeriksa ubin mana yang merupakan bagian dari kombinasi dan memanggil fungsi showEnd() pada ubin tersebut. Ini akan menampilkan grafik 'TileE'.
Milestone 1
Kami sekarang memiliki game yang berfungsi. Untuk sumber dari semua yang kami lakukan sejauh ini, periksa _milestone1.zip. Hasil!
Langkah 11: Fungsi AI
Saya harap Anda tidak bingung dengan semua skrip yang kami lakukan sejauh ini. kami akan melanjutkan dengan bagian AI dari game, yang membutuhkan beberapa fungsi. Pertama, mari kita buat beberapa perubahan dan tambahkan fungsi untuk menambahkan AI. Di TicTacToe.as, ubah baris berikut:
1 |
|
2 |
var game:Game = new Game(1); // Changed number of human players to 1 |
Selanjutnya, di Game.as, tambahkan variabel berikut:
1 |
|
2 |
private var _xlist:Array = new Array(); // keep track of all x tiles |
3 |
private var _olist:Array = new Array(); // keep track of all o tiles |
4 |
private var _flist:Array = new Array(); // keep track of all free tiles |
Dalam fungsi addVisuals(), tambahkan baris berikut (setelah Anda mendorong ubon ke Array _tiles):
1 |
|
2 |
_flist.push(tile1, tile2, tile3, tile4, tile5, tile6, tile7, tile8, tile9); // added all tiles to the free list |
Kemudian ubah fungsi nextTurn(e: Event = null) menjadi yang berikut:
1 |
|
2 |
public function nextTurn(e:Event = null):void |
3 |
{
|
4 |
if (!checkWin()) { |
5 |
if (e) { // added check for an event (we call the function once without marking a tile) |
6 |
if (e.currentTarget.isSet == "X") { // if the tile is set to X |
7 |
_xlist.push(e.currentTarget); // add it to the X list |
8 |
}
|
9 |
if (e.currentTarget.isSet == "O") { // if the tile is set to O |
10 |
_olist.push(e.currentTarget); // add it to the O list |
11 |
}
|
12 |
_flist.splice(_flist.indexOf(e.currentTarget), 1); // remove the tile from the free list |
13 |
}
|
14 |
left -= 1; |
15 |
if (left > 0) { |
16 |
turn += 1; |
17 |
if (turn % 2 == 0 && numPlayers == 1) handleAI(); // check if the PC should choose a tile |
18 |
} else { |
19 |
endGame(); |
20 |
}
|
21 |
}
|
22 |
}
|
Tentu saja, tambahkan juga fungsi handleAI() ke kelas Game.as:
1 |
|
2 |
private function handleAI():void |
3 |
{
|
4 |
trace(_xlist); |
5 |
trace(_olist); |
6 |
trace(_flist); |
7 |
}
|
Saat kami sekarang menguji Flash movie, setelah giliran pertama kami, kami melihat tiga daftar di Jendela Output kami. Panjang _xlist akan menjadi satu elemen (Anda memilih ubin untuk ditandai). _olist kosong (pemain dua belum mengambil ubin) dan _flist panjangnya delapan elemen (masih ada delapan ubin yang bisa dipilih). Kami akan menggunakan daftar ini di langkah berikutnya untuk membiarkan AI memilih ubin.
Langkah 12: AI Seberapa Cerdas?
Ada banyak cara untuk skrip perilaku komputer. Seperti yang mungkin Anda ketahui, jika kedua pemain memperhatikan dan mengetahui aturannya, permainan Tic-Tac-Toe selalu berakhir imbang. Kami hanya dapat membuat skrip semua situasi sehingga hasilnya selalu imbang atau menang untuk komputer. Tapi apa yang menyenangkan dari itu? Saya memilih untuk membuat AI yang tidak pintar, tetapi juga tidak bodoh. Ini adalah aturan yang akan kami tulis:
- untuk setiap ubin bebas, pilih jika ubin ini ditambah semua ubin yang sudah dipilih memenangkan permainan
- untuk setiap ubin bebas, pilih jika ubin ini ditambah semua ubin yang sudah dipilih memenangkan game
- jika tidak, pilih ubin bebas acak
Jadi komputer pertama-tama harus memeriksa apakah ia dapat memenangkan game. Jika komputer tidak bisa menang dengan ubin bebas ini, itu harus memeriksa apakah pemain manusia bisa menang dengan ubin ini. Ketika komputer maupun pemain manusia tidak bisa menang, itu harus memilih ubin acak. Kita bisa membuatnya berpikir di depan, dan alih-alih memilih ubin acak, biarkan ubin itu memilih dari ubin yang memungkinkan untuk memenangkan ronde berikutnya. Tetapi sekali lagi, kami tidak akan melakukan itu karena itu akan membuat game cukup membosankan.
Langkah 13: AI Mengaktifkan Ubin
Tidak ada yang lebih mudah dari langkah ini. Kita harus mengaktifkan ubin agar komputer dapat menandainya. Biasanya, ketika kami bermain dengan dua pemain, mouse digunakan untuk memicu pertukaran grafik dan mengirimkan event yang mengatur pergantian ke plus satu. Karena komputer tidak menggunakan MouseEvents, kita perlu mengubah dua fungsi di Tile.as sehingga mereka dapat digunakan dari file Game.as. Ini adalah fungsi-fungsi tersebut:
1 |
|
2 |
public function showGraphic(e:MouseEvent = null):void // Changed to public and set MouseEvent to null |
1 |
|
2 |
public function chooseGraphic(e:MouseEvent = null):void // Changed to public and set MouseEvent to null |
Dengan cara ini, kita dapat memanggil fungsi showGraphic() dan selectGraphic() (karena bersifat publik) tanpa pasokan MouseEvent (karena jika tidak dimasukan, kami menyediakan nul).
Langkah 14: Skrip AI
Mari kita menulis skrip untuk AI sekarang. Ini adalah tampilan file Game.as Anda. Saya sudah menulis komentar di samping setiap baris yang saya ubah.
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.MovieClip; |
5 |
import flash.events.Event; |
6 |
import flash.events.TimerEvent; // We now use a timer event |
7 |
import flash.utils.Timer; // And also a timer |
8 |
|
9 |
public class Game extends MovieClip |
10 |
{
|
11 |
public var numPlayers:uint; |
12 |
public var turn:uint; |
13 |
public var left:uint = 10; |
14 |
private var _grid:Grid = new Grid(); |
15 |
private var _tiles:Array = new Array(); |
16 |
private var _combos:Array = new Array(new Array(0, 1, 2), new Array(3, 4, 5), new Array(6, 7, 8), |
17 |
new Array(0, 3, 6), new Array(1, 4, 7), new Array(2, 5, 8), |
18 |
new Array(0, 4, 8), new Array(2, 4, 6)); |
19 |
private var _xlist:Array = new Array(); |
20 |
private var _olist:Array = new Array(); |
21 |
private var _flist:Array = new Array(); |
22 |
private var _choose:Boolean = false; // Added variable to check if the computer made a choice |
23 |
|
24 |
public function Game(num:uint):void |
25 |
{
|
26 |
numPlayers = num; |
27 |
turn = 0; |
28 |
addVisuals(); |
29 |
addTileListeners(); |
30 |
nextTurn(); |
31 |
}
|
32 |
|
33 |
private function addVisuals():void |
34 |
{
|
35 |
var tile1:Tile = new Tile(); |
36 |
var tile2:Tile = new Tile(); |
37 |
var tile3:Tile = new Tile(); |
38 |
var tile4:Tile = new Tile(); |
39 |
var tile5:Tile = new Tile(); |
40 |
var tile6:Tile = new Tile(); |
41 |
var tile7:Tile = new Tile(); |
42 |
var tile8:Tile = new Tile(); |
43 |
var tile9:Tile = new Tile(); |
44 |
tile1.x = tile4.x = tile7.x = tile1.y = tile2.y = tile3.y = 0; |
45 |
tile2.x = tile5.x = tile8.x = tile4.y = tile5.y = tile6.y = 100; |
46 |
tile3.x = tile6.x = tile9.x = tile7.y = tile8.y = tile9.y = 200; |
47 |
addChild(tile1); |
48 |
addChild(tile2); |
49 |
addChild(tile3); |
50 |
addChild(tile4); |
51 |
addChild(tile5); |
52 |
addChild(tile6); |
53 |
addChild(tile7); |
54 |
addChild(tile8); |
55 |
addChild(tile9); |
56 |
addChild(_grid); |
57 |
_tiles.push(tile1, tile2, tile3, tile4, tile5, tile6, tile7, tile8, tile9); |
58 |
_flist.push(tile1, tile2, tile3, tile4, tile5, tile6, tile7, tile8, tile9); |
59 |
}
|
60 |
|
61 |
private function addTileListeners():void |
62 |
{
|
63 |
for (var i:int = 0; i < _tiles.length; i++) { |
64 |
_tiles[i].addEventListener("TILE_ACTIVE", nextTurn); |
65 |
}
|
66 |
}
|
67 |
|
68 |
public function nextTurn(e:Event = null):void |
69 |
{
|
70 |
if (!checkWin()) { |
71 |
if (e) { |
72 |
if (e.currentTarget.isSet == "X") { |
73 |
_xlist.push(e.currentTarget); |
74 |
}
|
75 |
if (e.currentTarget.isSet == "O") { |
76 |
_olist.push(e.currentTarget); |
77 |
}
|
78 |
_flist.splice(_flist.indexOf(e.currentTarget), 1); |
79 |
}
|
80 |
left -= 1; |
81 |
if (left > 0) { |
82 |
turn += 1; |
83 |
if (turn % 2 == 0 && numPlayers == 1) handleAI(); |
84 |
} else { |
85 |
endGame(); |
86 |
}
|
87 |
}
|
88 |
}
|
89 |
|
90 |
private function handleAI():void |
91 |
{
|
92 |
_choose = false; // The computer didn't choose a tile yet |
93 |
for (var i:int = 0; i < _flist.length; i++) { |
94 |
_flist[i].removeButtonListeners(); // Remove all listeners on the free tiles so the human can't play |
95 |
}
|
96 |
var timer:Timer = new Timer(500, 1); // Set a timer (make it look like the computer 'thinks' for half a second) |
97 |
timer.addEventListener(TimerEvent.TIMER_COMPLETE, handleAIChoice); // Add a listener to the timer |
98 |
timer.start(); // Start the timer |
99 |
}
|
100 |
|
101 |
private function handleAIChoice(e:TimerEvent):void |
102 |
{
|
103 |
var i:int; |
104 |
for (i = 0; i < _flist.length; i++) { |
105 |
if (!_choose) { // If the computer didn't make a choice yet |
106 |
var ocheck:Array = _olist.concat(); // Make a copy of the _olist |
107 |
var xcheck:Array = _xlist.concat(); // Make a copy of the _xlist |
108 |
ocheck.push(_flist[i]); // Add a free tile to the ocheck array |
109 |
xcheck.push(_flist[i]); // Add a free tile to the xcheck array |
110 |
if (checkAIWin(ocheck)) break; // Check if with the free tile, o wins (this function also places the tile, if so) |
111 |
if (checkAIWin(xcheck)) break; // Check if with the free tile, x wins |
112 |
}
|
113 |
}
|
114 |
if (!_choose) { |
115 |
// If the computer didn't pick a tile yet (no win possible for x or o)
|
116 |
trace("Pick random spot"); |
117 |
var tile:int = Math.floor(Math.random() * _flist.length); // Pick a random index |
118 |
_flist[tile].showGraphic(); // Set the tile with the random index to show the graphic |
119 |
_flist[tile].chooseGraphic(); // Set the tile with the random index to choose the graphic |
120 |
}
|
121 |
for (i = 0; i < _flist.length; i++) { |
122 |
if (!checkWin()) _flist[i].addButtonListeners(); // Add all listeners on the free tiles again so the human can play |
123 |
}
|
124 |
}
|
125 |
|
126 |
private function checkAIWin(check:Array):Boolean |
127 |
{
|
128 |
for each (var combi in _combos) { |
129 |
var win = true; |
130 |
for each (var tile in combi) { |
131 |
if (check.indexOf(_tiles[tile]) == -1) { // Same function, but now we supply a check array |
132 |
win = false // If the check array doesnt include the tile of the combination, it's not a winning situation |
133 |
}
|
134 |
}
|
135 |
if (win) { |
136 |
// If there is a winning situation, choose the tile
|
137 |
trace("Block or check winning tile"); |
138 |
check[check.length - 1].showGraphic(); // We added the free tile at the end of the check array |
139 |
check[check.length - 1].chooseGraphic(); // So set the tile with index check.length - 1 to show/choose |
140 |
_choose = true; // Make sure we set _choose to true (all other checks in handleAIChoice will be skipped) |
141 |
break; |
142 |
}
|
143 |
}
|
144 |
return _choose; |
145 |
}
|
146 |
|
147 |
private function checkWin():Boolean |
148 |
{
|
149 |
var won:Boolean = false; |
150 |
for each (var combi in _combos) { |
151 |
var xwin = true; |
152 |
var owin = true; |
153 |
for each (var tile in combi) { |
154 |
xwin = xwin && _tiles[tile].isSet == "X"; |
155 |
owin = owin && _tiles[tile].isSet == "O"; |
156 |
}
|
157 |
if (xwin || owin) { |
158 |
endGame(combi); |
159 |
won = true; |
160 |
break; |
161 |
} else { |
162 |
won = false; |
163 |
}
|
164 |
}
|
165 |
return won; |
166 |
}
|
167 |
|
168 |
private function endGame(combi:Array = null):void |
169 |
{
|
170 |
for (var i:int = 0; i < _tiles.length; i++) { |
171 |
_tiles[i].removeButtonListeners(); |
172 |
if (combi) { |
173 |
for each (var j in combi) { |
174 |
if (i == j) _tiles[i].showEnd(); |
175 |
}
|
176 |
}
|
177 |
}
|
178 |
}
|
179 |
}
|
180 |
}
|
Ambil napas dalam-dalam. Perubahan pertama yang harus saya jelaskan adalah pada fungsi handleAI, yang memicu ketika komputer harus memilih ubin. Kami menggunakan variabel _choose di sini dan mengaturnya ke false (komputer belum memilih ubin). Kami kemudian menghapus semua listener dari ubin bebas. Dengan cara ini, pemain manusia tidak dapat mengklik kapan saja untuk bergerak ke komputer. Terakhir namun tidak kalah pentingnya, kami mengatur timer. Saya melakukan ini karena terlihat sedikit lebih keren jika komputer benar-benar "berpikir" tentang kepindahannya. Script untuk membuat pilihan akan dijalankan dalam beberapa milidetik, tetapi dengan cara ini Anda dapat membuatnya terlihat benar-benar membutuhkan sesuatu untuk dipikirkan.
Perubahan besar kedua adalah fungsi handleAIChoice. Saya menambahkan komentar yang harus menjelaskan semuanya di sini. Kami menggunakan trik concat() untuk membuat salinan _olist dan _xlist (var copy = original tidak menyalin array original! Jika Anda memodifikasi copy, Anda juga akan memodifikasi yang original). Bagian yang paling penting adalah panggilan ke fungsi checkWin(), yang kami berikan semua ubin X atau O yang sudah dipilih dengan ubin bebas (kami melakukan ini untuk setiap ubin bebas).
checkAIWin() terlihat seperti fungsi checkWin() yang kami buat sebelumnya, tapi kali ini kami menyediakan array. Kami tidak akan memeriksa semua ubin X atau O, tetapi untuk array ubin yang kami berikan (yang berarti: semua ubin X ditambah satu ubin bebas, atau semua ubin O ditambah satu ubin bebas). Jika array yang disediakan berisi semua ubin yang diperlukan untuk kombinasi yang menang, variabel win akan tetap true. Jika tidak, ini akan disetel ke false.
Dalam kasus win == true, ini artinya ubin bebas akan menciptakan situasi kemenangan. Komputer selalu ingin memilih ubin ini, karena jika itu membuat komputer menang, itu bagus. Dan jika ubin bebas memungkinkan pemain manusia menang, itu harus memblokir ubin bebas sehingga pemain manusia tidak bisa menang.
Milestone 2
Kami sekarang memiliki game yang berfungsi dengan AI. Untuk semua yang kami lakukan sejauh ini, periksa _milestone2.zip. Hasil!
Langkah 15: Menu Grafik
Buat dua Tombol untuk tombol "One Player" dan tombol "Two Players". Ekspor mereka untuk ActionScript dengan nama kelas B1 dan B2 (B untuk "Button"). Ini milik saya:
Langkah 16: Menambahkan fungsionalitas menu
Tentu saja, menu Anda harus ditambahkan ke stage dan bekerja. Ini cukup mudah (ya, Anda bisa santai, bagian yang sulit sudah selesai). Lihatlah file TicTacToe.as Anda dan tulis kode berikut:
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.MovieClip; |
5 |
import flash.events.Event; |
6 |
import flash.events.MouseEvent; |
7 |
|
8 |
public class TicTacToe extends MovieClip |
9 |
{
|
10 |
private var b1:B1 = new B1(); |
11 |
private var b2:B2 = new B2(); |
12 |
private var game:Game; |
13 |
|
14 |
public function TicTacToe():void |
15 |
{
|
16 |
b1.x = 120; |
17 |
b1.y = 60; |
18 |
b2.x = 180; |
19 |
b2.y = 240; |
20 |
addChild(b1); |
21 |
addChild(b2); |
22 |
b1.addEventListener(MouseEvent.CLICK, startGame); |
23 |
b2.addEventListener(MouseEvent.CLICK, startGame); |
24 |
addMenu(); |
25 |
}
|
26 |
|
27 |
private function startGame(e:MouseEvent):void |
28 |
{
|
29 |
b1.visible = false; |
30 |
b2.visible = false; |
31 |
game = (e.currentTarget == b1) ? new Game(1) : new Game(2); |
32 |
game.addEventListener("restart", addMenu); |
33 |
addChild(game); |
34 |
}
|
35 |
|
36 |
private function addMenu(e:Event = null):void |
37 |
{
|
38 |
if (game != null) { |
39 |
removeChild(game); |
40 |
game = null; |
41 |
}
|
42 |
b1.visible = true; |
43 |
b2.visible = true; |
44 |
}
|
45 |
}
|
46 |
}
|
Karena semua skrip di atas adalah baru, saya tidak menulis baris komentar apa pun. Pertama, kami membuat tiga variabel. Kami membuat tombol dan variabel game. Dalam konstruktor (TicTacToe()) kami mengatur posisi x dan y dari tombol kami. Pastikan Anda memposisikan tombol Anda sendiri pada koordinat yang benar. Kemudian kami menambahkan tombol ke daftar tampilan dan menambahkan listener MouseEvent. Terakhir, kami memanggil fungsi addMenu().
Di startGame(e: MouseEvent) kami mengatur kedua tombol menjadi visible = false. Dengan cara ini, kita tidak akan melihatnya lagi. Jika Anda ingin melewati bagian ini, Anda harus memastikan Grid MovieClip memiliki latar belakang yang solid. Anda tidak akan dapat melihat tombol itu, karena Anda menempatkan game di atasnya. Saya lebih suka memastikan (Anda mungkin ingin memposisikan game Anda di tempat lain) dan mengatur visibility ke false. Kami kemudian memeriksa tombol mana yang telah ditekan. Jika b1, kami ingin memanggil new Game(1) (1 pemain, vs komputer). Jika tidak, kami membutuhkan new Game(2) (2 pemain manusia). Lalu kami menambahkan event listener RESTART ke game, sehingga kami dapat memeriksa kapan game selesai. Terakhir tapi bukan yang akhir: tambahkan game ke daftar tampilan sehingga kita benar-benar bisa bermain.
Fungsi addMenu(e: Event = null) tidak banyak membantu. Ini dipanggil pertama kali kami membuka Flash Movie kami, dan setiap kali game berakhir. Pertama-tama memeriksa apakah ada game, dan jika demikian itu menghapusnya dari daftar tampilan dan mengatur game ke null. Selanjutnya mengatur visibilitas tombol menjadi true sehingga kita dapat mengkliknya lagi.
Langkah 17: Ini Tidak bekerja!
Saat Anda menguji game, itu berfungsi dengan baik. Tetapi ketika Anda menyelesaikan game pertama Anda, itu tidak menampilkan menu. Ini karena kami belum menambahkan dispatcher event RESTART ke file Game.as. Ayo lakukan itu. Dalam fungsi endGame(combi: Array = null), tambahkan baris berikut:
1 |
|
2 |
var timer:Timer = new Timer(1500, 1); |
3 |
timer.addEventListener(TimerEvent.TIMER_COMPLETE, restart); |
4 |
timer.start(); |
Ini memulai penghitung waktu 1,5 detik (sehingga Anda akan memiliki waktu untuk melihat siapa yang benar-benar menang). Selanjutnya, buat fungsi yang dipanggil oleh timer:
1 |
|
2 |
private function restart(e:TimerEvent):void |
3 |
{
|
4 |
dispatchEvent(new Event("restart")); |
5 |
}
|
Semua fungsi ini adalah mengirimkan event RESTART, yang kami tunggu di kelas TicTacToe. Game selesai!
Milestone 3
Kami sekarang memiliki game yang berfungsi dengan AI dan menu. Untuk semua yang kami lakukan sejauh ini, periksa _milestone3.zip. Hasil!
Langkah 18: Bisakah Anda Menemukan Bugnya?
Saya harap Anda masih bersama kami. Kami memang memiliki game yang berfungsi sekarang, tetapi jika Anda menguji game tersebut secara berlebihan, Anda mungkin telah menemukan bug. Komputer AI memeriksa setiap ubin bebas jika itu bisa menang dengan ubin itu. Jika tidak, ia memeriksa apakah bisa hilang oleh ubin itu. Jadi pikirkan: komputer dapat memilih ubin, dan bisa menang dengan ubin 4. Ini juga bisa kalah dengan ubin 2. Jadi ketika memeriksa ubin 2, itu akan memilih ubin itu karena akan kehilangan putaran berikutnya jika tidak , jangan pernah mencapai centang pada ubin 4 yang dengannya memenangkan putaran ini. Solusi: periksa dulu semua ubin bebas untuk situasi menang. Kemudian periksa situasi yang hilang. Ini akan menjadi fungsi handleAIChoice(e: TimerEvent) Anda yang baru (bagi yang pernyataan for pertama menjadi dua):
1 |
|
2 |
private function handleAIChoice(e:TimerEvent):void |
3 |
{
|
4 |
var i:int; |
5 |
for (i = 0; i < _flist.length; i++) { |
6 |
if (!_choose) { |
7 |
var ocheck:Array = _olist.concat(); |
8 |
ocheck.push(_flist[i]); |
9 |
if (checkAIWin(ocheck)) break; |
10 |
}
|
11 |
}
|
12 |
for (i = 0; i < _flist.length; i++) { |
13 |
if (!_choose) { |
14 |
var xcheck:Array = _xlist.concat(); |
15 |
xcheck.push(_flist[i]); |
16 |
if (checkAIWin(xcheck)) break; |
17 |
}
|
18 |
}
|
19 |
if (!_choose) { |
20 |
var tile:int = Math.floor(Math.random() * _flist.length); |
21 |
_flist[tile].showGraphic(); |
22 |
_flist[tile].chooseGraphic(); |
23 |
}
|
24 |
for (i = 0; i < _flist.length; i++) { |
25 |
if (!checkWin()) _flist[i].addButtonListeners(); |
26 |
}
|
27 |
}
|
Langkah 19: Saran Grafik
Meskipun ini tutorial, saya suka melihat orang-orang mempelajari sesuatu dan membangunnya. Untuk membantu Anda dalam perjalanan, saya akan menulis beberapa saran tentang hal-hal yang dapat Anda ubah dan/atau tambahkan.
Secara visual, Anda memiliki sedikit keterbatasan. Perhatikan bahwa Anda dapat mengedit MovieClips dari Grid, Ubin, dan Tombol. Jangan membatasi diri Anda pada satu frame: aset ini adalah MovieClips karena suatu alasan. Misalnya, Anda dapat membuat animasi burung terbang dan menggunakannya sebagai Ubin. Anda dapat membuat warna Grid berubah, biarkan Tombol Anda terpental, namai itu. Anda dapat membuat Loading MovieClip yang bagus dan menambahkannya ke game. Atur visibilitasnya ke false, dan menjadi true saat komputer "berpikir". Daripada mengakhiri permainan dengan jeda, perlihatkan gambar siapa yang menang dengan kembang api dan orang-orang bersorak. Jadilah kreatif.
Langkah 20: Saran Skrip
Meskipun Anda sudah memiliki game Tic-Tac-Toe yang berfungsi, Anda selalu dapat menambahkan lebih banyak fungsi dan mengubah fungsi yang sudah dimilikinya. Misalnya, alih-alih aturan yang saya berikan kepada Anda, Anda bisa membuat aturan baru untuk AI. Selalu pilih sudut pertama. Mulailah dengan ubin tengah jika tersedia. Atau biarkan komputer berpikir selangkah lebih maju - Anda setengah jalan di sana!
Saran lain yang bisa saya buat, adalah secara acak memutuskan siapa yang akan mulai ketika Anda bermain versus komputer. Ini mungkin terlihat cukup mudah, tetapi pastikan Anda memikirkannya. Itu dimulai dengan mengubah baris berikut di Game.as:
if (turn % 2 == 0 && numPlayers == 1) handleAI(); // Change turn % 2 == 0 to turn % 2 == randomBegin
Di konstruktor Game(num: uint), atur randomBegin menjadi 0 atau 1. Ini semua yang perlu Anda lakukan, tetapi lihat fungsi handleAIChoice kami. Dalam fungsi ini pertama-tama kita memeriksa situasi menang untuk O, kemudian untuk X. Ini benar ketika komputer bermain dengan ubin O, tetapi ketika Anda membuat bagian ini acak ... Jadi, mungkin mengubah dua pernyataan for menjadi fungsi dan memanggil mereka dalam urutan yang benar, tergantung pada randomBegin.
Kesimpulan
Saya sangat berharap Anda menyukai tutorial ini. Ini adalah pertama saya, saya mencoba untuk tetap sederhana tetapi menyenangkan dan saya harap saya berhasil. Jika Anda memiliki pertanyaan tentang tutorial ini, silakan tinggalkan komentar di sini. Saya juga ingin melihat apa yang Anda buat dengan bantuan tutorial ini, jadi jangan ragu untuk mengirim URL ke proyek Anda.
Terima kasih telah membaca dan menikmati Flash dan ActionScript!