Advertisement
  1. Game Development
  2. Game Development

Membuat Minesweeper Segi Enam

Scroll to top
Read Time: 15 min

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

Final product imageFinal product imageFinal product image
What You'll Be Creating

Dalam tutorial ini, saya akan memperkenalkan dunia game berbasis petak heksagonal menggunakan pendekatan termudah. Kamu akan belajar bagaimana mengubah array data dua dimensi menjadi susunan level heksagonal pada layar dan sebaliknya. Menggunakan informasi yang didapat, kita akan membuat game minesweeper heksagonal dengan dua susunan heksagonal yang berbeda.

Ini akan membantu kamu mulai menguasai board game heksagonal sederhana dan game puzzle dan menjadi titik awal untuk mempelajari pendekatan lebih rumit seperti axial atau sistem koordinat cubic hexagonal.

1. Petak dan susunan heksagonal

Dalam generasi game casual saat ini, kita tidak melihat banyak game yang menggunakan pendekatan berbasis petak heksagonal. Biasanya game seperti itu adalah game puzzle, board game, atau game strategi. Lalu, sebagian besar kebutuhan kita sudah terpenuhi oleh petak persegi biasa atau pendekatan isometric. Ini memunculkan pertanyaan "Kenapa kita perlu pendekatan heksagonal yang berbeda dan lebih rumit?". Kita cari tahu sekarang.

Keuntungan pendekatan heksagonal

Jadi apa yang membuat pendekatan berbasis petak heksagonal relevan saat kita memiliki pendekatan lain yang sudah disempurnakan? Saya akan sebutkan beberapa alasannya:

  • Lebih sedikit petak tetangga: Jika dibandingkan dengan grid persegi yang memiliki delapan petak tetangga, petak heksagonal hanya memiliki enam tetangga. Hal ini mengurangi komputasi yang dibutuhkan oleh algoritma yang kompleks.
  • Semua petak tetangga memiliki jarak yang sama: Pada petak persegi, empat tetangga diagonal lebih jauh dibandingkan tetangga horizontal atau vertikal. Tetangga yang memiliki jarak yang sama sangat membantu saat memperhitungkan heuristik dan mengurangi beban menggunakan dua metode berbeda untuk menghitung sesuatu yang tergantung pada posisi tetangga.
  • Keunikan: Belakangan ini, jutaan casual game datang dan pergi memperebutkan waktu pemain. Banyak game yang bagus tidak berhasil mendapatkan pemain, dan satu hal yang dijamin bisa mendapatkan perhatian pemain adalah keunikan. Sebuah game yang menggunakan pendekatan heksagonal akan mencolok secara visual dari yang lain, dan game tersebut akan terlihat lebih menarik untuk orang-orang yang bosan dengan mekanik gameplay yang biasa.

Saya rasa alasan terakhir ini cukup untuk membuatmu menguasai pendekatan baru ini. Menambahkan elemen gameplay yang unik pada logika game kamu bisa membuat semua perbedaan yang dibutuhkan dan membuat kamu bisa membuat game yang bagus.

Alasan lainnya sangat teknis dan hanya ada dampaknya saat kamu berhadapan dengan algoritma kompleks atau susunan petak yang sangat besar. Ada banyak aspek lain yang bisa ditulis sebagai keuntungan pendekatan heksagonal, tapi sebagian besar akan tergantung pada minat pemain yang bersangkutan.

Susunan Petak

Sebuah heksagon adalah bangun dengan enam sisi, dan sebuah heksagon yang semua sisinya memiliki panjang yang sama disebug heksagon regular/biasa. Secara teori, petak heksagonal yang kita miliki adalah heksagon biasa, tapi pada prakteknya bisa dipanjang atau diperlebar.

Yang menarik adalah bahwa sebuah heksagon bisa disimpan dengan dua cara, sudut yang tajam disusun vertikal atau horizontal. Saat sudut atas yang tajam disusun secara vertikal, disebut susunan horizontal. Dan ketika disusun horizontal, disebut susunan vertikal. Kamu bisa berpikir nama tersebut adalah kesalahan jika melihat penjelasan sebelumnya. Ini bukan kesalahan karena penamaan bukan berdasarkan posisi pojok yang tajam tapi bagaimana petak disusun pada grid. Gambar berikut menunjukkan bagaimana susunan tile yang berbeda dan tampilan susunan tersebut.

The vertical and horizontal hexagonal tile grid layoutThe vertical and horizontal hexagonal tile grid layoutThe vertical and horizontal hexagonal tile grid layout

Pilihan susunan sangat tergantung pada visual dan gameplay game yang kamu buat. Tapi pilihan tersbut tidak berhenti di sini karena masing-masing susunan tersebut diimplementasi dengan dua cara berbeda.

Pertimbangkan susunan grid heksagon horizontal. Masing-masing baris pada grid perlu digeser secara horizontal sejauh hexTileWidth/2. Artinya kita bisa memilih untuk menggeser pada baris ganjil atau baris genap. Jika kita juga menampilkan nilai baris dan kolom, varian tersebut akan muncul seperti gambar berikut.

Horizontal hexagonal layout with even odd offsetsHorizontal hexagonal layout with even odd offsetsHorizontal hexagonal layout with even odd offsets

Begitu pula, susunan vertikal bisa diimplementasi dalam dua cara dengan menggeser kolom sejauh hexTileHeight/2 seperti yang ditampilkan di bawah.

Vertical hexagonal layout showing even odd variationsVertical hexagonal layout showing even odd variationsVertical hexagonal layout showing even odd variations

2. Mengimplementasi susunan heksagonal

Dari titik ini dan seterusnya, harap mulai melihat source code yang disediakan dengan tutorial ini agar bisa mengerti lebih baik.

Pada gambar di atas, dengan menampilkan nilai baris dan kolom, kita bisa lebih mudah memvisualisasi hubungan langsung dengan array dua dimensi yang menyimpan data level. Misalnya kita memiliki array dua dimensi levelData seperti berikut.

1
var levelData=
2
[[0,0,0,0,0],
3
[0,0,0,0,0],
4
[0,0,0,0,0],
5
[0,0,0,0,0],
6
[0,0,0,0,0]
7
]

Agar lebih mudah digambarkan, saya akan menampilkan hasil yang diharapkan di sini dalam variasi vertikal dan horizontal.

simple hexagonal grid based on level data arraysimple hexagonal grid based on level data arraysimple hexagonal grid based on level data array

Kita mulai dengan susunan horizontal, seperti pada gambar di kiri. Di setiap baris, masih-masing petak yang bertetangga berjarak hexTileWidth. Baris alternatif digeser sejauh hexTileWidth/2. Perbedaan ketinggian vertikal antar masing-masing baris adalah hexTileHeight*3/4.

Untuk memahami bagaimana kita mendapat nilai tersebut untuk selisih ketinggian, kita perlu mempertimbangkan fakta bahwa bagian atas dan bawah dari heksagon yang disimpan secara horizontal adalah tepat bernilai hexTileHeight/4.

Ini artinya sebuah heksagon memiliki bagian persegi hexTileHeight/2 di tengah, bagian segitiga hexTileHeight/4 di atas, dan segitiga terbalik hexTileHeight/4 di bawah. Informasi ini cukup untuk membuat kode yang dibutuhkan untuk menampilkan grid heksagon pada layar.

1
var verticalOffset=hexTileHeight*3/4;
2
var horizontalOffset=hexTileWidth;
3
var startX;
4
var startY;
5
var startXInit=hexTileWidth/2;
6
var startYInit=hexTileHeight/2;
7
    
8
var hexTile;
9
for (var i = 0; i < levelData.length; i++)
10
{
11
    if(i%2!==0){
12
        startX=2*startXInit;
13
    }else{
14
        startX=startXInit;
15
    }
16
    startY=startYInit+(i*verticalOffset);
17
    for (var j = 0; j < levelData[0].length; j++)
18
    {
19
        if(levelData[i][j]!=-1){
20
            hexTile= new HexTile(game, startX, startY, 'hex',false,i,j,levelData[i][j]);
21
            hexGrid.add(hexTile);
22
        }    
23
        startX+=horizontalOffset;
24
    }    
25
}

Dengan prototipe HexTile, saya tambahkan beberapa fungsionalitas ke prototipe Phaser.Sprite yang membuatnya nbisa menampilkan nilai i dan j. Kode tersebut pada dasarnya menempakan sebuah tile heksagonal Sprite baru pada startX dan startY. Kode ini bisa diubah untuk menampilkan varian ofset genap dengan menghapus sebuah operator pada kondisi if seperti ini: if(i%2===0).

Untuk susunan vertikal (gambar pada bagian kanan), petak tetangga di setiap kolom digeser vertikal senilai hexTileHeight. Setiap kolom alternatif digeser senilai hexTileHeight/2. Dengan menerapkan logika yang kita gunakan untuk selisih jarak vertikal pada tampilan horizontal, kita bisa lihat bahwa selisih horizontal antar petak tetangga papda sebuahb aris pada susunan vertikal bernilai hexTileWidth*3/4. Kodenya adalah sebagai berikut.

1
var verticalOffset=hexTileHeight;
2
var horizontalOffset=hexTileWidth*3/4;
3
var startX;
4
var startY;
5
var startXInit=hexTileWidth/2;
6
var startYInit=hexTileHeight/2;  
7
var hexTile;
8
for (var i = 0; i < levelData.length; i++)
9
{
10
    startX=startXInit;
11
    startY=2*startYInit+(i*verticalOffset);
12
    for (var j = 0; j < levelData[0].length; j++)
13
    {
14
        if(j%2!==0){
15
            startY=startY+startYInit;
16
        }else{
17
            startY=startY-startYInit;
18
        }
19
        if(levelData[i][j]!=-1){
20
            hexTile= new HexTile(game, startX, startY, 'hex', true,i,j,levelData[i][j]);
21
            hexGrid.add(hexTile);
22
        }
23
        startX+=horizontalOffset;
24
    }    
25
}

Dengan cara yang sama seperti susunan horizontal, kita bisa berubah ke varian ofset genap dengan menghilangkan operator ! pada kondisi if atas. Saya menggunakan Group dari Phaser untuk mengumpulkan semua hexTiles, dengan nama hexGrid. Agar lebih sederhana, saya menggunakan titik tengah petak heksagonal sebagai anchor, jika tidak, kita perlu mempertimbangkan ofset gambarnya juga.

Satu hal yang perlu diperhatikan adalah lebar dan tinggi petak pada susunan horizontal berbeda dengan lebar dan tinggi petak pada susunan vertikal. Tapi jika menggunakan gambar yang sama untuk kedua susunan tersebut, kita bisa memutar gambar petak 90 derajat dan menukar nilai lebar dan tinggi petak.

3. Menemukan indeks array sebuah petak heksagonal

Logika perubahan dari array ke posisi layar cukup sederhana, namun proses sebaliknya tidak mudah. Bayangkan bahwa kita perlu menemukan indeks array dari petak yang kita ketuk. Kode untuk mencapai ini tidak cantik, dan biasanya didapat dari berbagai percobaan dan kegagalan.

Jika kita pertimbangkan susunan horizontal, kelihatannya bagian tengah dari petak heksagonal bisa membantu kita menentukan nilai j dengan membagi nilai x dengan hexTileWidth dan mengambil nilai bilangan bulat. Tapi kecuali kita tahu nilai i, kita tidak tahu apakah kita berada pada baris ganjil atau genap. Nilai perkiraan dari i bisa didapat dengan membagi nilai y dengan hexTileHeight*3/4.

Sekarang kita masuk ke bagian yang rumit dari petak heksagonal: bagian segitiga atas dan bawah. Gambar di bawah akan membantu menjelaskan masalah kita saat ini.

a horizontally laid hexagonal tile split into regionsa horizontally laid hexagonal tile split into regionsa horizontally laid hexagonal tile split into regions

Bagian 2, 3, 5, 6, 8, dan 9 membentuk satu petak. Bagian paling rumit adalah menentukan apakah posisi ketuk pada 1/2 atau 3/4 atau 7/8 atau 9/10. Untuk ini kita perlu memeriksa masing-masing area segitiga dan memeriksanya terhadap sudut dari sisi yang miring.

Sudut atau nilai kemiringan tersebut bisa didapat dari tinggi dan lebar dari masing-masing area, yaitu hexTileHeight/4 dan hexTileWidth/2. Berikut adalah fungsi yang melakukannya.

1
function findHexTile(){
2
    var pos=game.input.activePointer.position;
3
    pos.x-=hexGrid.x;
4
    pos.y-=hexGrid.y;
5
    var xVal = Math.floor((pos.x)/hexTileWidth);
6
    var yVal = Math.floor((pos.y)/(hexTileHeight*3/4));
7
    var dX = (pos.x)%hexTileWidth;
8
    var dY = (pos.y)%(hexTileHeight*3/4); 
9
    var slope = (hexTileHeight/4)/(hexTileWidth/2);
10
    var caldY=dX*slope;
11
    var delta=hexTileHeight/4-caldY;
12
    
13
    if(yVal%2===0){
14
       //correction needs to happen in triangular portions & the offset rows

15
       if(Math.abs(delta)>dY){
16
           if(delta>0){//odd row bottom right half

17
                xVal--;
18
                yVal--;
19
           }else{//odd row bottom left half

20
                yVal--;
21
           }
22
       }
23
    }else{
24
        if(dX>hexTileWidth/2){// available values don't work for even row bottom right half

25
            if(dY<((hexTileHeight/2)-caldY)){//even row bottom right half

26
                yVal--;
27
            }
28
        }else{
29
           if(dY>caldY){//odd row top right & mid right halves

30
               xVal--;
31
           }else{//even row bottom left half

32
               yVal--;
33
           }
34
        }
35
    }
36
   pos.x=yVal;
37
   pos.y=xVal;
38
   return pos;
39
}

Pertama, kita dapatkan nilai xVal dan yVal dengan cara yang sama seperti untuk grid persegi. Lalu kita dapatkan nilai horizontal (dX) dan vertikal (dY) setelah menghilangkan ofset pengali petak. Dengan nilai-nilai ini, kita coba untuk mengetahui apakah titik ketuk berada di dalam area segitiga yang rumit tersebut.

Jika ditemukan, kita buat perubahan pada nilai awal xVal dan yVal. Seperti yang saya bilang sebelumnya, kode ini tidak cantik atau sederhana. Cara termudah untuk memahaminya adalah dengan memanggil findHexTile pada saat mouse bergerak, dan menempatkan console.log pada setiap kondisi yang ada dan menggerakkan mouse ke berbagai area pada sebuah petak heksagonal. Dengan begini kamu bisa lihat bagaimana area dalam sebuah heksagon ditangani.

Perubahan kode untuk susunan vertikal ditampilkan di bawah ini.

1
function findHexTile(){
2
    var pos=game.input.activePointer.position;
3
    pos.x-=hexGrid.x;
4
    pos.y-=hexGrid.y;
5
    var xVal = Math.floor((pos.x)/(hexTileWidth*3/4));
6
    var yVal = Math.floor((pos.y)/(hexTileHeight));
7
    var dX = (pos.x)%(hexTileWidth*3/4);
8
    var dY = (pos.y)%(hexTileHeight); 
9
    var slope = (hexTileHeight/2)/(hexTileWidth/4);
10
    var caldX=dY/slope;
11
    var delta=hexTileWidth/4-caldX;
12
    if(xVal%2===0){
13
        if(dX>Math.abs(delta)){// even left

14
            
15
        }else{//odd right

16
            if(delta>0){//odd right bottom

17
                xVal--;
18
                yVal--;
19
            }else{//odd right top

20
                xVal--;
21
            }
22
        }
23
    }else{
24
        if(delta>0){
25
            if(dX<caldX){//even right top

26
                xVal--;
27
            }else{//odd mid

28
               yVal--; 
29
            }
30
        }else{//current values wont help for even right bottom

31
           if(dX<((hexTileWidth/2)-caldX)){//even right bottom

32
                xVal--;
33
           }
34
        } 
35
    }
36
   pos.x=yVal;
37
   pos.y=xVal;
38
   return pos;
39
}

4. Menemukan tetangga

Setelah kita menemukan petak yang kita ketuk, kita temukan enam petak tetangganya. Hal ini sangat mudah dipecahkan begitu kita analisa grid secaravisual. Lihatlah susunan horizontal berikut.

odd and even rows of horizontal layout when a middle tile has ij set to 0odd and even rows of horizontal layout when a middle tile has ij set to 0odd and even rows of horizontal layout when a middle tile has ij set to 0

Gambar di atas menampilkan baris ganjil dan genap ada grid heksagon horizontal saat petak tengan memiliki nilai 0 untuk i dan j. Dari gambar tersebut, terlhat jelas jika sebuah baris itu ganjil, maka petak i,j akan memiliki tetangga i,j-1, i-1,j-1, i-1,j, i,j+1, i+1,j, dan i+1,j-1. Pada baris genap, tetangga untuk petak i,j adalah i, j-1i-1,ji-1,j+1i,j+1i+1,j+1, dan i+1,j. Ini bisa dihitung manual dengan mudah.

Kita periksa gambar serupa untuk kolom ganjil dan genap pada grid heksagonal vertikal.

odd and even columns of vertical layout when a middle tile has ij set to 0odd and even columns of vertical layout when a middle tile has ij set to 0odd and even columns of vertical layout when a middle tile has ij set to 0

Pada kolom ganjil, petak i,j akan memiliki tetangga i,j-1i-1,j-1i-1,ji-1,j+1i,j+1, dan i+1,j. Begitu pula untuk kolom genap, tetangganya adalah i+1,j-1i,j-1i-1,ji,j+1i+1,j+1, dan i+1,j.

5. Minesweeper Heksagonal

Dengan pengetahuan di atas, kita bisa mencoba membuat game minesweeper pada dua susunan yang berbeda. Kita bedah fitur sebuah game minesweeper.

  1. Akan ada sejumlah N ranjau yang disembunyikan pada grid.
  2. Jika kita mengetuk petak dengan ranjau, game akan berakhir.
  3. Jika kita mengetuk petak dengan tetangga berupa ranjau, petak tersebut akan menampilkan jumlah ranjau di sekitar petak tersebut.
  4. Jika kita mengetuk petak tanpa tetangga berupa ranjau, semua petak terhubung yang juga tidak memiliki ranjau akan dibuka.
  5. Kita bisa mengetuk dan menahan untuk menandai sebuah petak sebagai ranjau.
  6. Game akan selesai ketika kita membuka semua petak tanpa ranjau.

Kita bisa dengan mudah menyimpan nilai pada array levelData untuk menunjukkan sebuah ranjau. Cara yang sama juga bisa digunakan untuk mengisi nilai ranjau di tetangga sebuah petak.

Saat game dimulai, kita mengisi secara acak array levelData dengan sejumlah N ranjau. Setelah ini kita perbarui nilai semua petak tetangga. Kita akan menggunakan metode rekursif untuk membuka semua petak kosong saat pemain mengetuk petak yang tidak memiliki tetangga ranjau.

Data Level

Kita perlu membuat grid heksagonal yang menarik, seperti pada gambar di bawah.

horizontal hexagonal minesweeper gridhorizontal hexagonal minesweeper gridhorizontal hexagonal minesweeper grid

Ini bisa dicapai dengan hanya menampilkan sebagian dari array levelData. Jika kita menggunakan -1 sebagai nilai tile yang tidak digunakan dan 0 sebagai nilai petak yang digunakan, maka levelData tampilan di atas akan seperti berikut ini.

1
//horizontal tile shaped level

2
var levelData=
3
[[-1,-1,-1,0,0,0,0,0,0,0,-1,-1,-1],
4
[-1,-1,0,0,0,0,0,0,0,0,-1,-1,-1],
5
[-1,-1,0,0,0,0,0,0,0,0,0,-1,-1],
6
[-1,0,0,0,0,0,0,0,0,0,0,-1,-1],
7
[-1,0,0,0,0,0,0,0,0,0,0,0,-1],
8
[0,0,0,0,0,0,0,0,0,0,0,0,-1],
9
[0,0,0,0,0,0,0,0,0,0,0,0,0],
10
[0,0,0,0,0,0,0,0,0,0,0,0,-1],
11
[-1,0,0,0,0,0,0,0,0,0,0,0,-1],
12
[-1,0,0,0,0,0,0,0,0,0,0,-1,-1],
13
[-1,-1,0,0,0,0,0,0,0,0,0,-1,-1],
14
[-1,-1,0,0,0,0,0,0,0,0,-1,-1,-1],
15
[-1,-1,-1,0,0,0,0,0,0,0,-1,-1,-1]];

Saat memproses array dalam loop, kita hanya menambahkan petak heksagonal saat nilai leveData adalah 0. Untuk susunan vertikal, levelData yang sama bisa digunakan, tapi kita perlu melakukan transpose pada array. Berikut adalah fungsi yang bisa melakukannya untukmu.

1
levelData=transpose(levelData);
2
//...

3
function transpose(a) {
4
    return Object.keys(a[0]).map(
5
        function (c) { return a.map(function (r) { return r[c]; }); }
6
        );
7
}

Menambahkan ranjau dan memperbarui tetangga

Awalnya, levelData hanya memiliki dua nilai, -1 dan 0, dengan hanya area bernilai 0 yang kita gunakan. Untuk menandai petak yang berisi ranjau, kita gunakan nilai 10.

Petak heksagonal kosong bisa memiliki maksimum enam ranjau di sekitarnya karena hanya memilki enam petak tetangga. Kita bisa menyimpan informasi ini pada levelData setelah kita menambahkan semua ranjau. Pada dasarnya, sebuah indeks levelData yang bernilai 10 adalah sebuah ranjau, dan jika berisi nilai dari 0 sampai 6, nilai tersebut menujukkan jumlah ranjau pada petak tetangga. Setelah mengisi ranjau dan memperbarui tetangga, jika sebuah elemen array masih bernilai 0, itu berarti petak tersebut kosong tanpa tetangga berisi ranjau.

Kita bisa menggunakan fungsi berikut untuk memenuhi kebutuhan kita.

1
function addMines(){
2
    var tileType=0;
3
    var tempArray=[];
4
    var newPt=new Phaser.Point();
5
    for (var i = 0; i < levelData.length; i++)
6
    {
7
        for (var j = 0; j < levelData[0].length; j++)
8
        {
9
            tileType=levelData[i][j];
10
            if(tileType===0){
11
                newPt=new Phaser.Point();
12
                newPt.x=i;
13
                newPt.y=j;
14
                tempArray.push(newPt);
15
            }
16
        }
17
    }
18
    for (var i = 0; i < numMines; i++)
19
    {
20
        newPt=Phaser.ArrayUtils.removeRandomItem(tempArray);
21
        levelData[newPt.x][newPt.y]=10;//10 is mine

22
        updateNeighbors(newPt.x,newPt.y);
23
    }
24
}
25
function updateNeighbors(i,j){//update neighbors around this mine

26
    var tileType=0;
27
    var tempArray=getNeighbors(i,j);
28
    var tmpPt;
29
    for (var k = 0; k < tempArray.length; k++)
30
    {
31
        tmpPt=tempArray[k];
32
        tileType=levelData[tmpPt.x][tmpPt.y];
33
        levelData[tmpPt.x][tmpPt.y]=tileType+1;
34
    }
35
}

Untuk setiap ranjau yang ditambahkan pada addMines, kita menambahkan nilai array pada semua petak tetangga. Fungsi getNeighbors tidak akan mengembalikan petak yang di luar area atau jika berisi ranjau.

Logika Ketuk

Ketika pemain mengetuk sebuah petak, kita perlu menemukan elemen array yang bersangkutan menggunakan fungsi findHexTile yang sudah dijelaskan sebelumnya. Jika indeks petak tersebut ada pada area aktif, kita hanya perlu membandingkan nilai pada indeks array tersebut untuk mengetahui apakah petak tersebut ranjau atau petak kosong.

1
function onTap(){
2
    var tile= findHexTile();
3
    
4
     if(!checkforBoundary(tile.x,tile.y)){
5
        if(checkForOccuppancy(tile.x,tile.y)){
6
            if(levelData[tile.x][tile.y]==10){
7
                //console.log('boom');

8
                var hexTile=hexGrid.getByName("tile"+tile.x+"_"+tile.y);
9
                if(!hexTile.revealed){
10
                    hexTile.reveal();
11
                    //game over

12
                }
13
            }
14
        }else{
15
            var hexTile=hexGrid.getByName("tile"+tile.x+"_"+tile.y);
16
                    
17
            if(!hexTile.revealed){
18
                if(levelData[tile.x][tile.y]===0){
19
                    //console.log('recursive reveal');

20
                    recursiveReveal(tile.x,tile.y);
21
                }else{
22
                    //console.log('reveal');

23
                    hexTile.reveal();
24
                    revealedTiles++;
25
                }
26
                
27
            }
28
        }
29
    }
30
    infoTxt.text='found '+revealedTiles +' of '+blankTiles;
31
}

Kita mencatat jumlah total petak kosong menggunakan variabel blankTiles dan jumlah petak yang terbuka dengan revealedTiles. Begitu kedua nilai tersebut bernilai sama, kita memenangkan permainan.

Ketika kita mengetuk petak dengan nilai pada array 0, kita perlu membuka semua petak kosong yang terhubung secara rekursif. Hal ini dicapai dengan fungsi recursiveReveal, yang menerima petak yang diketuk pemain.

1
function recursiveReveal(i,j){
2
    var newPt=new Phaser.Point(i,j);
3
    var hexTile;
4
    var tempArray=[newPt];
5
    var neighbors;
6
    while (tempArray.length){
7
        newPt=tempArray[0];
8
        var neighbors=getNeighbors(newPt.x,newPt.y);
9
        
10
        while(neighbors.length){
11
            newPt=neighbors.shift();
12
            hexTile=hexGrid.getByName("tile"+newPt.x+"_"+newPt.y);
13
            if(!hexTile.revealed){
14
                hexTile.reveal();
15
                revealedTiles++;
16
                if(levelData[newPt.x][newPt.y]===0){
17
                    tempArray.push(newPt);
18
                }
19
            }
20
        }
21
        newPt=tempArray.shift();//it seemed one point without neighbor sometimes escapes the iteration without getting revealed, catch it here

22
        hexTile=hexGrid.getByName("tile"+newPt.x+"_"+newPt.y);
23
        if(!hexTile.revealed){
24
            hexTile.reveal();
25
            revealedTiles++;
26
        }
27
    }
28
}

Pada fungsi ini, kita menemukan tetangga setiap petak dan membuka nilai petak tersebut, sembari menambahkan petak tetangga ke sebuah array. Kita terus melakukan ini dengan elemen berikutnya di array sampai array tersebut kosong. Rekursi akan berhenti ketika menemui elemen array yang berisi ranjau, yang dipastikan dengan fakta bahwa getNeighbors tidak akan mengembalikan petak dengan ranjau.

Menandai dan membuka petak

Kamu pasti melihat saya menggunakan hexTile.reveal(), yang dimungkinkan dengan membuat prototipe HexTile yang menyimpan sebagian besar atribut yang berhubungan dengan petak heksagonal kita. Saya menggunakan fungsi reveal untuk menampilkan nilai petak dalam teks dan menentukan warna petak. Begitu pula, fungsi toggleMark digunakan untuk menandai petak sebagai ranjau saat kita ketuk dan tahan. HexTile juga memilki atribut revealed yang mencatat apakah sebuah petak sudah diketuk dan terbuka atau tidak.

1
HexTile.prototype.reveal=function(){
2
    this.tileTag.visible=true;
3
    this.revealed=true;
4
    if(this.type==10){
5
        this.tint='0xcc0000';
6
    }else{
7
        this.tint='0x00cc00';
8
    }
9
}
10
HexTile.prototype.toggleMark=function(){
11
    if(this.marked){
12
       this.marked=false; 
13
       this.tint='0xffffff';
14
    }else{
15
        this.marked=true;
16
        this.tint='0x0000cc';
17
    }
18
}

Lihat minesweeper heksagonal dengan susunan horizontal di bawah. Ketuk untuk membuka petak, dan ketuk-tahan untuk menandari ranjau. Tidak ada game over saat ini, tapi jika kamu membuka petak bernilai 10, maka permainan akan berakhir!

Perubahan untuk versi vertikal

Saya menggunakan gambar petak heksagonal yang sama untuk kedua orientasi, saya memutar Sprite untuk susunan vertikal. Kode di bawah ini adalah isi prototipe HexTile.

1
if(isVertical){
2
    this.rotation=Math.PI/2;
3
}

Logika minesweeper tetap sama untuk susunan grid heksagonal vertikal dengan perbedaan pada findHextile dan getNeighbors yang sekarang perlu mengakomodasi perbedaan susunan. Seperti yang disebutkan sebelumnya, kita juga perlu menggunakan transpose pada array level dengan layout loop yang bersangkutan.

Lihatlah versi vertikal di bawah ini.

Sisa kode pada source ini cukup sederhana. Saya minta kamu coba menambahkan fitur restart, menang, dan game over.

Kesimpulan

Pendekatan game berbasis petak heksagonal menggunakan array dua dimensi adalah pendekatan yang paling mudah. Pendekatan yang lebih menarik mencakup mengubah sistem koordinat ke jenis lain menggunakan berbagai persamaan.

Yang paling penting adalah koordinat axial dan cubic. Akan ada tutorial lanjutan yang akan membahas pendekatan ini. Sementara itu saya sarankan untuk membaca artikel detail dari Amit tentang grid heksagonal.

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.