Membuat Game dengan Megaman-Inspired di Construct 2
() translation by (you can also view the original English article)
Saya akan memandu Anda melalui penciptaan game shooter / platformer yang terinspirasi dari Megaman. Kita akan lebih fokus pada aspek pemotretan gameplay daripada platformnya. Dalam tutorial ini saya akan menggunakan Construct 2 sebagai alat untuk membuat game, tapi saya akan menjelaskan logika menggunakan pseudocode sehingga Anda bisa mengikuti tutorial ini dalam bahasa atau mesin pilihan Anda.
Agar fokus pada implementasi gameplay, saya tidak akan menjelaskan setiap fitur construct 2; Saya akan berasumsi bahwa Anda mengetahui dasar-dasar seperti memuat sprite, tabrakan dasar, atau pemutar suara. Dengan dasar-dasar ini, marilah mulai membuat game.
Siapkan Artwork
Hal pertama yang pertama, kita perlu memiliki sprite untuk game kita. Untungnya, opengameart telah menyelamatkan kita dengan seni game legal mereka yang snagat hebat. Kita membutuhkan empat set sprite; satu pahlawan, satu musuh, dan ubin untuk platform.
- Hero (by Redshrike):
http://opengameart.org/content/xeon-ultimate-smash-friends - Enemy (also by Redshrike):
http://opengameart.org/content/fighting-robot-for-ultimate-smash-friends - Tiles (by robotality):
http://opengameart.org/content/prototyping-2d-pixelart-tilesets
Untuk menggunakannya di platform Construct 2, saya memangkas sprite pahlawan ke bingkai individu menggunakan GIMP.
Gerakan Dasar
Saya akan menggunakan perilaku platform Construct 2 untuk sisa tutorial sehingga saya dapat fokus pada bagian shooting dan AI dari game, yang menjadi fokus utama dari tutorial ini.

Basic Movement Explained
Jika Anda menggunakan bahasa lain, atau ingin menerapkan platform dasar Anda dari pada menggunakan platform built-in. Anda hanya perlu menggunkan kode dalam tahapan ini jika Anda tidak ingin menggunakan platformConstruct 2's built-in.
Untuk memulianya, kita perlu untuk mempertimbangkan tiga cara untuk berpindah; berjalan ke arah kanan, berjalan ke arah kiri, ataupun melompat. Setiap frame akan diperbarui simulasi gamemya.
1 |
number moveSpeed = 50; |
2 |
|
3 |
function update() |
4 |
{
|
5 |
moveHero(); |
6 |
}
|
Untuk memperbarui karakter pemain, kita menerapkan perpindahan dasar seperti
1 |
function moveHero() |
2 |
{
|
3 |
// player is pressing this key down
|
4 |
if (keyDown(KEY_LEFT)) |
5 |
{
|
6 |
hero.x -= moveSpeed * deltaTime; |
7 |
hero.animate("walkLeft"); |
8 |
}
|
9 |
|
10 |
if (keyDown(KEY_RIGHT)) |
11 |
{
|
12 |
hero.x += moveSpeed * deltaTime; |
13 |
hero.animate("walkRight"); |
14 |
}
|
15 |
|
16 |
if (keyDown(KEY_UP)) |
17 |
{
|
18 |
hero.jump(); |
19 |
hero.animate("jump"); |
20 |
}
|
21 |
|
22 |
// a key that was just unpressed
|
23 |
if (keyReleased(KEY_LEFT)) |
24 |
{
|
25 |
hero.animate("standLeft"); |
26 |
}
|
27 |
|
28 |
if (keyReleased(KEY_RIGHT)) |
29 |
{
|
30 |
hero.animate("standRight"); |
31 |
}
|
32 |
}
|
Saya menggunakan fungsi lain untuk melakukan lompatan karena melompat bukan hanya masalah mengubah nilai y tapi juga menghitung gravitasi. Kami juga akan memiliki fungsi yang akan merespon apakah sebuah kunci baru saja diluncurkan, untuk mengembalikan animasi pahlawan kita menjadi animasi bertahan.
Mari kita bicara tentang bagaimana membuat pemain melompat. Pahlawan itu perlu tahu apakah dia saat ini sedang melompat atau tidak, dan juga apakah dia saat ini jatuh atau tidak. Jadi kita akan mendeklarasikan dua variabel baru: isJumping danisFalling. Secara default, keduanya salah, yang berarti sang pahlawan berdiri di atas panggung.
Untuk melakukan lompatan kita harus terlebih dahulu mengecek apakah kedua nilai itu salah, dan kemudian membuat kode isjump menjadi benar.
1 |
Function jump() |
2 |
{
|
3 |
if (!isJumping && !isFalling) |
4 |
{
|
5 |
isJumping = True |
6 |
}
|
7 |
}
|
Agar sang pahlawan bisa melompat, kita butuh variabel yang disebut jumpPower dan gravity. Nilai default the jumpPower adalah -20 dan gravitasi adalah 1. Logikanya adalah menambahkan nilai daya loncat ke posisi Y pahlawan dan menambahkan gaya gravitasi untuk memberi nilai daya.
Kami melakukan ini setiap pada tahap. Mungkin ini bukan fisika gravitasi paling realistis yang ada, tapi game tidak perlu realistis, mereka hanya perlu dipercaya, karena itulah beberapa game memiliki lompatan manusia super dan lompatan ganda. Kode di bawah ini termasuk dalam fungsi pembaruan. Kode berikut termasuk dalam fungsi update.
1 |
If (isJumping || isFalling) |
2 |
{
|
3 |
hero.y += hero.jumpPower; |
4 |
hero.jumpPower += hero.gravity; |
5 |
}
|
6 |
|
7 |
// eventually jump power will be greater than zero, and that means the hero is falling
|
8 |
|
9 |
if (hero.jumpPower >= 0) |
10 |
{
|
11 |
isJumping = False; |
12 |
isFalling = True; |
13 |
}
|
14 |
|
15 |
// to make a stop to the fall, do something when the hero overlaps the platform
|
16 |
|
17 |
if (hero.isOverlapping(platform1)) |
18 |
{
|
19 |
// platform1 is the platform that our hero can step on
|
20 |
// resets the variable to their default value
|
21 |
isJumping = False; |
22 |
isFalling = False; |
23 |
hero.jumpPower = -20; |
24 |
}
|
25 |
|
26 |
// and then there's the free fall, when player falls over the edge of a platform
|
27 |
if (!hero.isOverlapping(platform1) && hero.jumpPower < 0 && !isJumping) |
28 |
{
|
29 |
// !hero.isOverlapping(platform1) checks whether or not our hero is standing on a platform
|
30 |
// and if jumpPower is less than zero and the player is not currently jumping, then that means
|
31 |
// he's falling
|
32 |
|
33 |
// setting these two values like this will make the player fall.
|
34 |
hero.jumpPower = 0; |
35 |
isFalling = true; |
36 |
}
|
Buatlah perilaku platform built-in 2 mereplikasi kode contoh di atas, yang hanya memberi bantuan kepada mereka yang bekerja dalam bahasa lain.
Menerapkan Penembakan
sampailah pada tahap penembakan dari sebuah game. Di seri Megaman ada tiga jenis tembakan: tembakan normal, tembakan bermuatan, dan tembakan energi tertingi.
tembakan normal cukup jelas. Charged shots adalah tembakan yang dibebankan terlebih dahulu sebelum dilepaskan, tembakan bermuatan ini datang dalam dua jenis: setengah diisi, dan terisi penuh. Serangan bermuatan ini lebih kuat dari tembakan normal, dengan terisi penuh maka ini menjadi yang terkuat.
Tembakan energi tinggi adalah tembakan dengan kekuatan yang diperoleh pemain setelah mengalahkan masing-masing atasan. Kerusakannya sama dengan tembakan normal tapi memiliki sifat khusus yang tidak dimiliki tembakan biasa.
Sekarang kita tahu tipe masing-masing tembakan, mari kita mulai untuk membuatnya. Pertama mari kita lihat logika di balik bagaimana kita menggunakan setiap tembakan. Di sini kita asumsikan bahwa tombol Z pada keyboard digunakan untuk menembakkan tembakan. Kami akan menerapkan dua perilaku yang berbeda:
- Sekarang kita tahu tipe masing-masing tembakan, mari kita mulai untuk membuatnya. Pertama mari kita lihat logika di balik bagaimana kita menggunakan setiap tembakan. Di sini kita asumsikan bahwa tombol Z pada keyboard digunakan untuk menembakkan tembakan. Kami akan menerapkan dua perilaku yang berbeda:
- Tembakan bermuatan: pemain menekan Z. Peluru normal pertama akan ditembak. Animasi akan berubah menjadi penembakan sebelum segera beralih ke animasi berdiri. Jika Z terus ditekan maka efek pengisian akan ditambahkan di atas animasi bermain (berdiri, berjalan). · Jika tombol Z dilepaskan dalam waktu kurang dari 5 detik sejak pengisian pertama, maka peluru yang setengah terisi akan ditembak. Jika tombol Z dilepaskan setelah 5 detik, peluru yang terisi penuh akan ditembak.
- Tembakan energi tinggi: pahlawan kita pertama-tama harus melengkapi peluru yang dia dapatkan setelah mengalahkan atasan. Setelah melengkapi, pemain akan menekan tombol lain untuk menembakkan peluru ini. Perilaku peluru ini bervariasi, dan perlu dikodekan secara unik untuk setiap peluru.
Sekarang, mari kita mulai meng-kode. Karena pahlawan kita bisa menembak ke kiri dan kanan kita perlu tahu arah mana yang sedang dia hadapi. Mari kita nyatakan sebuah variabel baru yang disebut menghadap yang menyimpan nilai string apakah pahlawan itu menghadap ke kiri atau kanan.
1 |
String facing = "right"; // which direction the hero is currently facing |
2 |
function moveHero() |
3 |
{
|
4 |
// player is pressing this key down
|
5 |
if (keyDown(KEY_LEFT)) |
6 |
{
|
7 |
hero.x -= moveSpeed * deltaTime; |
8 |
hero.animate("walkLeft"); |
9 |
|
10 |
facing = "left"; |
11 |
}
|
12 |
|
13 |
if (keyDown(KEY_RIGHT)) |
14 |
{
|
15 |
hero.x += moveSpeed * deltaTime; |
16 |
hero.animate("walkRight"); |
17 |
|
18 |
facing = "right"; |
19 |
}
|
20 |
|
21 |
// ... the continuation of moveHero() function goes here
|
22 |
}
|
23 |
|
24 |
function update() |
25 |
{
|
26 |
// ...the update code that we previously wrote goes here...
|
27 |
|
28 |
// player press this key once
|
29 |
if (keyPressed(KEY_Z)) |
30 |
{
|
31 |
if (facing == "right") |
32 |
{
|
33 |
hero.animate("Shoot"); |
34 |
|
35 |
// we will add shooting function here later
|
36 |
}
|
37 |
else if (facing == "left") |
38 |
{
|
39 |
hero.animate("Shoot"); |
40 |
hero.mirrorSprite(); // this function flips the sprite horizontally |
41 |
}
|
42 |
}
|
43 |
|
44 |
if (keyReleased(KEY_Z)) |
45 |
{
|
46 |
if (facing == "right") |
47 |
{
|
48 |
hero.animate("standRight"); |
49 |
}
|
50 |
else if (facing == "left") |
51 |
{
|
52 |
hero.animate("standLeft"); |
53 |
hero.mirrorSprite(); // we need to call this again because the sprite was mirrored |
54 |
// if we don't mirror the sprite again, standLeft will look like standRight
|
55 |
}
|
56 |
}
|
57 |
}
|



Sebelum kita menembak peluru, kita perlu melihat properti yang dimiliki peluru tersebut:
- Kekuasaan: kekuatan serangan peluru, kerusakan yang akan ditanggungnya terhadap musuh
- Kecepatan: seberapa cepat peluru pergi
- Sudut: sudut penembakan, menentukan arah peluru.
Properti ini akan berbeda untuk setiap peluru. Secara khusus, properti energi akan berbeda. Sudut properti biasanya hanya satu dari dua nilai; apakah peluru itu ditembak ke kanan atau kiri, kecuali jika itu adalah peluru energi tinggi yang mungkin tertembak pada sudut yang unik.
Variasi penembakan akan dibahas nanti jadi sekarang saya hanya akan menyelesaikan penembakna dasar. Berikut ini adalah potongan kode yang dapat menembakkan peluru.
1 |
// first, we create a function that creates a new bullet
|
2 |
Function shoot(string pathToSprite, number bulletPower, number bulletSpeed, number bulletAngle) |
3 |
{
|
4 |
myBullet = new Bullet(pathToSprite); |
5 |
myBullet.power = bulletPower; |
6 |
|
7 |
// the bullet class or object has two private variables that moves it according to its angle
|
8 |
// more explanation to these two lines need more math, so I choose not to explain
|
9 |
// I assume your engine of choice have a way to move an object according to its angle
|
10 |
ySpeed = Math.sin(bulletAngle) * bulletSpeed; |
11 |
xSpeed = Math.cos(bulletAngle) * bulletSpeed; |
12 |
}
|
13 |
|
14 |
// this is Bullet class' function that's called every frame, this moves the bullet according to its angle
|
15 |
function moveBullet() |
16 |
{
|
17 |
x += xSpeed * deltaTime; |
18 |
y += ySpeed * deltaTime; |
19 |
}
|
20 |
|
21 |
// and this is the modification to our previous update() function
|
22 |
function update() |
23 |
{
|
24 |
// ...the update code that we previously wrote goes here...
|
25 |
|
26 |
// player press this key once
|
27 |
if (keyPressed(KEY_Z)) |
28 |
{
|
29 |
if (facing == "right") |
30 |
{
|
31 |
hero.animate("Shoot"); |
32 |
hero.shoot("path/to/sprite.png", 10, 400, 0); |
33 |
|
34 |
}
|
35 |
else if (facing == "left") |
36 |
{
|
37 |
hero.animate("Shoot"); |
38 |
hero.mirrorSprite(); // this function flips the sprite horizontally |
39 |
hero.shoot("path/to/sprite.png", 10, 400, 180); // the angle is 180 so that the bullet goes left |
40 |
}
|
41 |
}
|
42 |
|
43 |
// .. the continuation of update code goes here...
|
44 |
}
|




Penembakan Bermuatan
Beberapa peluru bisa lebih bertenaga dibanding yang lain. Untuk membuat tembakan bermuatan, kami memerlukan variabel bernama chargedTime, yang akan bertambah setiap detik jika pemain menahan Z turun, dan akan kembali ke nol saat peluru ditembakkkan. Perubahan pada kode pembaruan adalah sebagai berikut:
1 |
// player just released z key
|
2 |
if (keyReleased(KEY_Z)) |
3 |
{
|
4 |
if (chargedTime > 0 && chargedTime <= 5) |
5 |
{
|
6 |
if (facing == "right") |
7 |
{
|
8 |
hero.animate("Shoot"); |
9 |
hero.shoot("path/to/halfChargedBullet.png", 20, 400, 0); |
10 |
chargedTime = 0; |
11 |
|
12 |
}
|
13 |
else if (facing == "left") |
14 |
{
|
15 |
hero.animate("Shoot"); |
16 |
hero.mirrorSprite(); // this function flips the sprite horizontally |
17 |
hero.shoot("path/to/halfChargedBullet.png", 20, 400, 180); |
18 |
chargedTime = 0; |
19 |
|
20 |
}
|
21 |
}
|
22 |
else if (chargedTime > 5) |
23 |
{
|
24 |
if (facing == "right") |
25 |
{
|
26 |
hero.animate("Shoot"); |
27 |
hero.shoot("path/to/fullChargedBullet.png", 40, 400, 0); |
28 |
chargedTime = 0; |
29 |
|
30 |
}
|
31 |
else if (facing == "left") |
32 |
{
|
33 |
hero.animate("Shoot"); |
34 |
hero.mirrorSprite(); // this function flips the sprite horizontally |
35 |
hero.shoot("path/to/fullChargedBullet.png", 40, 400, 180); |
36 |
chargedTime = 0; |
37 |
|
38 |
}
|
39 |
}
|
40 |
|
41 |
if (facing == "right") |
42 |
{
|
43 |
hero.animate("standRight"); |
44 |
}
|
45 |
else if (facing == "left") |
46 |
{
|
47 |
hero.animate("standLeft"); |
48 |
hero.mirrorSprite(); // we need to call this again because the sprite was mirrored |
49 |
// if we don't mirror the sprite again, standLeft will look like standRight
|
50 |
}
|
51 |
}
|
52 |
|
53 |
// player is pressing this key down
|
54 |
if (keyDown(KEY_Z)) |
55 |
{
|
56 |
// this is the function that adds the value of chargedTime every second
|
57 |
// this keyDown block of code will be run every frame, which is less than a second
|
58 |
// your engine of choice should have a way to tell whether a second has passed or not
|
59 |
addChargedTime(); |
60 |
}
|









Tokoh pahlawan baru kita bergerak ke kiri, kanan, dan melompat sesuai masukan kita, dan juga menembakkan peluru, apakah normal, setengah diisi, atau terisi penuh.
Menerapkan Musuh
Kita sekarang telah mengendalikan pahlawan. Mari kita menyebutnya Xeon demi kesederhanaan. Dia bisa melakukan beberapa gerakan dasar seperti berjalan, melompat, dan menembak. Itu hebat! Tapi apa gunanya kemampuan menembak tanpa ada yang bisa ditembak kan? Itulah mengapa kali ini kita akan membuat musuh pertama kita.
Mari desain atribut musuh kita sebelum kita mulai mengkodenya.
-
Kesehatan: seberapa sehat musuh kita tentukan berapa banyak tembakan (dan jenisnya) yang dibutuhkan untuk menghancurkannya.
-
Kekuatan: Kekuatan serangan musuh, berapa banyak kerusakan yang terjadi pada pemain kita.
-
ShotAngle: ke arah mana musuh menembak peluru, bisa dibiarkan atau di kanan atau di mana saja yang kita inginkan.
Ada banyak hal yang kita butuhkan untuk musuh kita, sekarang mari kita buat kelas / objek musuh.
Kelas / objek musuh dengan kelas pemain / objek, kecuali musuh tidak mendengarkan masukan pemain. Karena itu kita perlu mengganti bagian mana pahlawan kita mendengarkan masukan pemain, untuk musuh AI / logika.
Serangan AI Musuh
Sebagai permulaan, mari kita tangani tembakan dasar AI dari musuh. Musuh akan menembak pemain saat melihat pemain.
Untuk menentukan apakah musuh "melihat 'pemain, kita perlu mendefinisikan sebuah variabel untuk objek musuh yang disebut menghadap yang merupakan string yang menyimpan satu dari dua nilai," kiri "atau" kanan ".
Musuh juga membutuhkan semacam penglihatan, itulah sebabnya mengapa kita akan membuat variabel lain yang disebut jangkauan. Jika pemain berada dalam kisaran ini maka itu berarti musuh "melihat" pemain. The pseudocode adalah sebagai berikut:
1 |
function boolean checkSees() |
2 |
{
|
3 |
if (facing == "left" && hero.x >= enemy.x -- range) |
4 |
{
|
5 |
return true; |
6 |
}
|
7 |
|
8 |
if (facing == "right" && hero.x <= enemy.x + range) |
9 |
{
|
10 |
return true; |
11 |
}
|
12 |
|
13 |
return false; |
14 |
}
|



fungsi checkSees ()
Mungkin Anda telah melihat sesuatu dalam pseudocode ini: yaitu tidak mempertimbangkan posisi pahlawan, jadi musuh tetap akan menembaki pahlawan bahkan jika mereka berada di panggung dengan ketinggian yang berbeda.
Untuk saat ini ini sudah cukup, karena membuat algoritma garis pandang ada diluar lingkup tutorial ini. Dalam permainan Anda sendiri, Anda mungkin ingin menambahkan toleransi Y pada fungsi di atas yang akan memeriksa apakah posisi pahlawan berada di antara dua titik yang menentukan ketinggian musuh.
Membuat Musuh Menembak
Pseudocode untuk penembakan yang dilakukan musuh adalah sebagai berikut:
1 |
// can be in update() or somewhere else that's executed every frame
|
2 |
function update() |
3 |
{
|
4 |
if (checkSees()) |
5 |
{
|
6 |
shoot("path/to/bulletSprite.png", enemyPower, 400, shotAngle); |
7 |
}
|
8 |
}
|
Seperti yang bisa Anda lihat, fungsi shoot () musuh sama dengan pemain. Dibutuhkan jalur sprite, kekuatan serangan, kecepatan peluru, dan sudut penembakan sebagai parameter.
Gerakan AI Musuh
Kapan musuh beralih dari menghadap ke kiri untuk menghadap ke kanan? Bagi pahlawan kita, kita menggunakan input pemain untuk mengubah arah wajah pahlawan kita. Bagi musuh kita, kita memiliki dua pilihan: gunakan beberapa jenis timer untuk beralih menghadap ke arah setiap beberapa detik sambil membiarkan musuh berdiri diam, atau mintalah musuh untuk berjalan ke tempat tertentu dan kemudian beralih menghadap ke arahnya dan kemudian berjalan ke tempat lain. untuk beralih menghadap ke arahnya lagi.
Metode kedua ini bisa digunakan sebagai patroli AI. Tentu saja, kita bisa membuat musuh berjalan dalam satu arah dan tidak pernah kembali.
Metode pseudocode untuk metode pertama adalah sebagai berikut:
1 |
function switchingAI() |
2 |
{
|
3 |
// elapsedTime() is a function that counts how many seconds has passed since its value is reset
|
4 |
// I assume your engine of choice have this kind of functionality
|
5 |
if (elapsedTime() > 4.0) |
6 |
{
|
7 |
if (facing == "left") |
8 |
{
|
9 |
facing = "right"; |
10 |
shotAngle = 0; |
11 |
}
|
12 |
|
13 |
if (facing == "right") |
14 |
{
|
15 |
facing = "left"; |
16 |
shotAngle = 180; |
17 |
}
|
18 |
|
19 |
enemy.mirrorSprite(); // also flip the sprite horizontally |
20 |
resetTime(); // resets the time that counts in elapsedTime() |
21 |
}
|
22 |
}
|



Musuh Berpatroli AI
Untuk membuat AI yang patroli, kita perlu membuat dua benda tak terlihat yang berada di ujung kedua rute patroli musuh, dan membuat musuh bergerak ke arah lain jika bertabrakan dengan mereka.



Sekarang mari kita tulis pseudocode untuk AI patroli musuh:
1 |
function patrollingAI() |
2 |
{
|
3 |
if (facing == "right") |
4 |
{
|
5 |
walkRight(); // the same as the one in player object / class |
6 |
|
7 |
if (collidesWith(rightPatrolBorder)) |
8 |
{
|
9 |
facing = "left"; |
10 |
enemy.mirrorSprite(); |
11 |
}
|
12 |
}
|
13 |
|
14 |
if (facing == "left") |
15 |
{
|
16 |
walkLeft(); |
17 |
|
18 |
if (collidesWith(leftPatrolBorder)) |
19 |
{
|
20 |
facing = "right"; |
21 |
enemy.mirrorSprite(); |
22 |
}
|
23 |
}
|
24 |
}
|



Setelah ini, musuh akan berpatroli di antara dua titik seperti yang kita inginkan.
Untuk mengatur AI yang digunakan musuh, kita akan menambahkan satu variabel lagi dengan tipe string untuk musuh kita: AI musuh. Ini akan menentukan apa yang AI gunakan setiap frame, seperti:
1 |
if (enemyAI == "switching") |
2 |
{
|
3 |
switchingAI(); |
4 |
}
|
5 |
else if (enemyAI == "patrolling") |
6 |
{
|
7 |
patrollingAI(); |
8 |
}
|
Tentu Anda bisa menambahkan lebih banyak jenis AI musuh jika Anda mau.
Variasi Penembakan
Mari kita lanjutkan tentang bagaimana kita bisa membuat variasi tembakan untuk pemain dan musuh. Kami membuat variasi tembakan dengan mengubah dua hal: sudut pengambilan gambar, dan jumlah tembakan peluru.
Dengan cara ini kita bisa melakukan tembakan peluru yang sederhana, atau tembakan peluru tiga arah. Sebelum kita melakukan ini kita akan membuat variabel lain ke objek musuh / kelas bernama shotAI, yaitu sebuah string. Kami akan menggunakan ini di checkSees kami () memeriksa apakah blok, di mana musuh akan menembak. Perubahan pada blok kode akan menjadi seperti ini:
1 |
// can be in update() or somewhere else that's executed every frame
|
2 |
function update() |
3 |
{
|
4 |
if (checkSees()) |
5 |
{
|
6 |
if (shotAI == "simple") |
7 |
{
|
8 |
shoot("path/to/bulletSprite.png", enemyPower, 400, shotAngle); |
9 |
}
|
10 |
|
11 |
if (shotAI == "threeBullets") |
12 |
{
|
13 |
shootThreeBullets(); |
14 |
}
|
15 |
}
|
16 |
}
|



Tentu saja, nama AI dan jenis tembakan yang akan ditembak musuh terserah Anda, ini hanyalah sebuah contoh.
Sekarang, mari pelajari lebih dalam apa yang ada di dalam fungsi shootThreeBullets ().
1 |
Function shootThreeBullets() |
2 |
{
|
3 |
if (facing == "right") |
4 |
{
|
5 |
shoot("path/to/bulletSprite.png", enemyPower, 400, 0); // this bullet goes straight to the right |
6 |
shoot("path/to/bulletSprite.png", enemyPower, 400, 330); // this goes up by 30 degrees |
7 |
shoot("path/to/bulletSprite.png", enemyPower, 400, 30); // this goes down by 30 degrees |
8 |
}
|
9 |
|
10 |
if (facing == "left") |
11 |
{
|
12 |
shoot("path/to/bulletSprite.png", enemyPower, 400, 180); // this bullet goes straight to the left |
13 |
shoot("path/to/bulletSprite.png", enemyPower, 400, 210); // this goes up by 30 degrees |
14 |
shoot("path/to/bulletSprite.png", enemyPower, 400, 150); // this goes down by 30 degrees |
15 |
}
|
16 |
}
|
Jika Anda tidak yakin mengapa 0 pergi ke kanan dan 180 pergi ke kiri, itu karena arah 0 derajat lurus ke sisi kanan layar, 90 derajat mengarah ke sisi bawah layar, dan seterusnya sampai ini menabrak 360 derajat. Begitu Anda tahu nilai apa yang muncul dan mengarah kemana, Anda bisa menciptakan variasi tembakan Anda sendiri.
Kita juga bisa membuat variabel shotAI untuk pemain, tapi saya lebih suka kita namai itu selectedShot, karena pemain kita akan memilih peluru bukan diprogram dari awal.
Logika di megaman adalah setiap saat megaman mengalahkan bos, dia mendapat kekuatan bos itu sebagai tembakan baru. Saya akan mencoba menciptakan logika itu. Untuk melakukan ini, kita membutuhkan sebuah array yang berisi tembakan pemain, termasuk tembakan normal. Pseudocode seperti ini:
1 |
var shotArr = ["normalShot", "boss1", "boss2"]; |
2 |
var shotIndex = 0; |
3 |
var selectedShot = "normalShot"; |
4 |
|
5 |
function update() |
6 |
{
|
7 |
// this is the block of code in player's update function where the player shoots a bullet
|
8 |
|
9 |
// this function changes the bullet that the player shoots
|
10 |
changeBullet(); |
11 |
|
12 |
// player press this key once
|
13 |
if (keyPressed(KEY_Z)) |
14 |
{
|
15 |
if (facing == "right") |
16 |
{
|
17 |
hero.animate("Shoot"); |
18 |
if ( selectedShot == "normalShot") |
19 |
{
|
20 |
hero.shoot("path/to/sprite.png", 10, 400, 0); |
21 |
}
|
22 |
else if ( selectedShot == "boss1") |
23 |
{
|
24 |
// add codes to shoot the kind of shot that the player received after defeating boss 1
|
25 |
}
|
26 |
}
|
27 |
}
|
28 |
}
|
29 |
|
30 |
function changeBullet() |
31 |
{
|
32 |
// changes shotIndex based on button pressed
|
33 |
if (keyPressed(KEY_E)) |
34 |
{
|
35 |
shotIndex += 1; |
36 |
}
|
37 |
|
38 |
if (keyPressed(KEY_Q)) |
39 |
{
|
40 |
shotIndex -= 1; |
41 |
}
|
42 |
|
43 |
// fix shotIndex if it's out of array's length
|
44 |
if (shotIndex == shotArr.length) |
45 |
{
|
46 |
shotIndex = 0; |
47 |
}
|
48 |
|
49 |
if (shotIndex < 0) |
50 |
{
|
51 |
shotIndex = shotArr.length -- 1; |
52 |
}
|
53 |
|
54 |
selectedShot = shotArr[shotIndex]; |
55 |
}
|

Kita perlu melacak dua variabel baru:






Kami akan mendorong elemen baru untuk shotArr saat pemain tersebut mengalahkan bos.
Upgrade Peluru Pemain
Sama seperti shootThreeBullet musuh (), Anda bisa menjadi kreatif dan menciptakan variasi tembakan Anda sendiri. Karena ini adalah peluru pahlawan, mari kita berikan sesuatu yang spesial.
Mari kita membuat satu jenis tembakan agar efektif melawan bos tertentu, sehingga bisa mengatasi lebih banyak kerusakan. Untuk melakukan ini, kita akan membuat sebuah variabel untuk objek peluru yang bernama strongAgainst yaitu variabel tipe string lain yang berisi nama bos bahwa peluru ini efektif melawannya. Kami akan menambahkan penawaran ini lebih banyak fungsi kerusakan saat kita membahas bagian bos dari game.
Kesehatan dan Kematian
Di sinilah semua variasi tembakan yang kita buat benar-benar mulai menjadi masalah. Di sinilah pahlawan kita merusak dan membunuh musuh, dan sebaliknya.
Untuk memulai, mari kita membuat variabel untuk kedua pahlawan dan objek musuh, bernama kesehatan yang merupakan int, dan variabel lain untuk pahlawan yang disebut variable lives (kehidupan). Mari kita lihat pseudocode:
1 |
if (bullet.collidesWith(hero)) |
2 |
{
|
3 |
hero.health -= bullet.power; |
4 |
createExplosion(); // for now we don't have an explosion sprite, so this will act as a reminder |
5 |
}
|
6 |
|
7 |
// check if the hero is dead
|
8 |
if (hero.health <= 0) |
9 |
{
|
10 |
hero.lives -= 1; // decreases hero's total number of lives. |
11 |
destroyHero(); |
12 |
}
|
Kita akan membuat pseudocode yang sama untuk perusakan musuh, seperti:
1 |
if (bullet.collidesWith(enemy)) |
2 |
{
|
3 |
enemy.health -= bullet.power; |
4 |
createExplosion(); |
5 |
}
|
6 |
|
7 |
if (enemy.health <= 0) |
8 |
{
|
9 |
destroyEnemy(); |
10 |
}
|



GUI Bar Kesehatan
Sekarang, jika saya berhenti begitu saja maka itu tidak akan menarik. Jadi saya akan membuat sprite persegi panjang di pojok kiri atas layar yang bertindak sebagai bar kesehatan pahlawan kita.
Panjang bar kesehatan ini akan berubah tergantung pada kesehatan pahlawan kita saat ini. Rumus untuk mengubah batas kesehatan bar adalah ini:
1 |
// this is in the update function
|
2 |
healthBar.width = (hero.health / hero.maxHealth) * 100; |



Kita membutuhkan satu variabel lagi untuk pahlawan kita yang disebut maxHealth; nilai penuh kesehatan pahlawan kita. Untuk saat ini nilai ini tidak bisa diubah tapi mungkin di masa depan kita bisa membuat item yang meningkatkan jumlah maxHealth pahlawan.
Membuat Dunia Game
Sekarang kita telah menciptakan variasi pahlawan, musuh, dan tembakan kita, kita perlu membuat beberapa tingkatan dan bos.
Untuk memiliki beberapa tingkat berarti pada beberapa titik dalam permainan pemain akan mencapai satu atau lebih pos pemeriksaan yang mengalihkan permainan dari tingkat 1-1 ke tingkat 1-2 ke level 1-3 dan seterusnya sampai mereka mencapai bos.
Saat pemain mati di suatu tempat di level 1-2 dia tidak perlu memutar ulang semua permainan sejak awal dari level 1-1. Bagaimana saya melakukan ini? Pertama, kita akan membuat level, saya tidak akan menjelaskan banyak tentang perancangan level, tapi inilah contoh level dalam Build 2.



Gambar pada bagian pojok kiri atas adalah lapisan HUD. Ini akan merambat, mengikuti pahlawan ketika game dimainkan.
Doors and Checkpoints
Satu sprite yang harus Anda perhatikan adalah sprite hijau di bagian kanan atas dari level. Ini adalah pos pemeriksaan di level ini saat pemain bertabrakan dengan itu kita akan mentransfer game ke level 1-2.
Untuk menangani beberapa tingkat, kita memerlukan tiga variabel: currentLevel, levelName, dan nextLevel.
Variabel currentLevel dibuat di objek pahlawan / kelas. LevelName dibuat di adegan game (tingkat) objek untuk setiap level. Variabel nextLevel dibuat pada objek sprite hijau.
Logikanya adalah sebagai berikut: ketika pahlawan bertabrakan dengan sprite hijau (saya menyebutnya greenDoor), kita akan mengubah level kita ke adegan game dimana variabel levelName sama dengan variabel nextLevel. Setelah kita mengubah level, kita akan mengubah nilai variabel currentreevel pahlawan menjadi sama dengan levelame pada adegan gameName. Inilah pseudocodenya:
1 |
// this is inside game's update function
|
2 |
if (hero.collidesWith(greenDoor)) |
3 |
{
|
4 |
changeLevelTo(greenDoor.nextLevel); |
5 |
}
|



Menginisiasi Level Baru
Berikut adalah pseudocode untuk menangani saat level berikutnya dimuat dan siap dimainkan.
1 |
// this is the function that's triggered when the new level is loaded
|
2 |
function onStart() |
3 |
{
|
4 |
hero.currentLevel = scene.LevelName; |
5 |
hero.x = startPos.x; |
6 |
hero.y = startPos.y; |
7 |
}
|



Posisi Awal Pemain
Sekarang setelah kita berubah ke level yang baru, saya akan menjelaskan sprite oranye di belakang tokoh kita di gambar desain tingkat di atas. Sprite oranye itu adalah objek yang saya sebut startPos. Ini digunakan untuk menandai posisi awal setiap level.
Kami mengacu pada objek ini saat sang pahlawan baru saja berubah level atau mati, sehingga kita tahu dimana harus menelurkannya.
Inilah pseudocode untuk penanganan saat sang pahlawan mati:
1 |
// the function that's triggered when the hero is destroyed
|
2 |
Function onDestroyed() |
3 |
{
|
4 |
// revives hero if the hero still have lives.
|
5 |
If (hero.lives > 0) |
6 |
{
|
7 |
var newHero = new Hero(); |
8 |
newHero.x = startPos.x; |
9 |
newHero.y = startPos.y; |
10 |
}
|
11 |
}
|
Sekarang kita bisa memiliki banyak level dan kita juga bisa menelurkan ulang saat sang pahlawan telah meninggal.
Anda bisa menciptakan level sebanyak yang Anda mau, atau mungkin malah menciptakan dua objek greenDoor di level mana salah satunya kembali ke level 1-1 jika pemain memecahkan teka-teki dengan cara yang salah.
pertempuran Bos
Akhirnya saatnya menerapkanbos itu sendiri. Untuk membuat level bos sesederhana membuat level lain yang akan menelurkan bos yang bukan merupakan musuh biasa.
Bagian yang sulit adalah menciptakan AI untuk bos karena masing-masing bos akan memiliki AI yang unik. Jadi untuk memudahkan untuk memahami dan menduplikat banyak bos, saya akan membuat AI bos yang bergantung pada waktu setelah mereka melahirkan. Yang berarti mereka akan melakukan A selama x detik, lalu ganti menjadi B selama beberapa detik kemudian lakukan C selama z detik sebelum kembali ke A. Pseudocode akan terlihat seperti ini:
1 |
// this code is inside the boss update function, so it's executed every frame
|
2 |
if (elapsedTime() > 2.0) |
3 |
{
|
4 |
// this if block is executed for 3 seconds, because the difference in time with the if block below
|
5 |
// is three seconds.
|
6 |
BossShot1(); // shot variation to be executed this time |
7 |
}
|
8 |
else if (elapsedTime() > 5.0) |
9 |
{
|
10 |
bossShot2(); |
11 |
}
|
12 |
else if (elapsedTime() > 6.0) |
13 |
{
|
14 |
bossShot3(); |
15 |
}
|
16 |
|
17 |
if (elapsedTime() > 7.0) |
18 |
{
|
19 |
// reset the time so the boss executes the first action again
|
20 |
resetsTime(); |
21 |
}
|



Definisi fungsi tembakan bos ada di bawah. Jangan ragu untuk mengubahnya agar sesuai dengan apa yang Anda inginkan untuk pertarungan bos:
1 |
function bossShot1() |
2 |
{
|
3 |
// a simple straight shot
|
4 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); // shotAngle is 180 |
5 |
}
|
6 |
|
7 |
function bossShot2() |
8 |
{
|
9 |
// a three direction bullets shot
|
10 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); |
11 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle + 30); |
12 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle - 30); |
13 |
}
|
14 |
|
15 |
function bossShot3() |
16 |
{
|
17 |
// for this one, I'm going to make a circle shot, so the bullets will form a circle
|
18 |
for (var i = 0; i <= 9; i++) |
19 |
{
|
20 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle |
21 |
}
|
22 |
}
|
Terserah Anda untuk menambahkan variasi tembakan ke rutinitas bos. Variabel yang digunakan oleh objek musuh bos sama dengan objek musuh, kecuali bos yang tidak menggunakan variabel enemyAI dan shotAI, karena keduanya akan ditangani jika blok di atas tergantung pada waktu yang telah berlalu.
Boss musuh juga memiliki variabel yang objek yang tidak dimiliki musuh. Ini disebut rewardShot, yang merupakan string. Variabel ini memegang nama bos yang ditembak pemain yang akan didapat setelah mengalahkan bos (yang ada dalam variabel array shotArr).
Ini akan memungkinkan pemain untuk "belajar" serangan bos seperti yang dijelaskan sebelumnya. Untuk menambahkan jenis tembakan ini ke tangkapan tangkapan pemain, kita perlu menambahkan kode berikut setelah musuh mati:
1 |
function bossShot1() |
2 |
{
|
3 |
// a simple straight shot
|
4 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); // shotAngle is 180 |
5 |
}
|
6 |
|
7 |
function bossShot2() |
8 |
{
|
9 |
// a three direction bullets shot
|
10 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle); |
11 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle + 30); |
12 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, shotAngle - 30); |
13 |
}
|
14 |
|
15 |
function bossShot3() |
16 |
{
|
17 |
// for this one, I'm going to make a circle shot, so the bullets will form a circle
|
18 |
for (var i = 0; i <= 9; i++) |
19 |
{
|
20 |
bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle |
21 |
}
|
22 |
}
|
Untuk menerapkan jenis peluru tambahan agar pemain Anda dapat menikmatinya, yang perlu Anda lakukan adalah menambahkan kode yang sesuai untuk setiap hadiah. Buatlah penghargaan atas hasil yang Anda buat, seperti yang telah kami lakukan sebelumnya.
Selamat!
Anda telah menyelesaikan tutorial tentang cara membuat permainan platfor metroidvania Megaman di Contruct 2. Selanjutnya Anda hanya perlu membangun permainan Anda berdasarkan pada apa yang telah Anda pelajari di sini untuk membuatnya menjadi milik Anda sendiri. Tambahkan lebih banyak tipe musuh, level lebih, powerups dan lainnya. Semoga berhasil dan selamat bersenang - senang.