Indonesian (Bahasa Indonesia) translation by Sap (you can also view the original English article)
Dalam tutorial ini kita akan membuat game di mana tujuannya adalah untuk mencegah benda lain bertabrakan dengan kursor Anda. Kita tidak akan menggunakan metode hitTestObject()
bawaan Flash; alih-alih kita akan menulis rutinitas deteksi tabrakan kita sendiri.
Setiap beberapa minggu, kami mengunjungi kembali beberapa posting favorit pembaca dari sepanjang sejarah situs. Tutorial ini pertama kali diterbitkan pada bulan Februari 2011.
Pratinjau Hasil Akhir
Mari kita lihat hasil akhir yang akan kita upayakan:
Langkah 1: Memulai
Buat file Flash baru (ActionScript 3.0)

Atur dimensi panggung ke 500x500px dan FPS ke 32.



Langkah 2: Kelas Bola
Kelas ini akan berisi semua data yang terkait dengan satu bola. Sebuah bola memiliki _mass
, _radius
, sebuah _xSpeed
dan _ySpeed
. Jadi kita akan membuat properti untuk masing-masing. Di konstruktor, kita melewati massa, sudut dan kecepatan bola. Karena kelas akan ditautkan ke objek tampilan, kita dapat mengambil jari-jari bola kita dengan membagi lebar objek tampilan dengan 2. _xSpeed
dan _ySpeed
dapat dihitung dengan menggunakan fungsi sinus dan cosinus sederhana.
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.Stage |
5 |
import flash.display.Sprite |
6 |
import flash.events.Event |
7 |
|
8 |
public class Ball extends Sprite |
9 |
{
|
10 |
private var _radius:Number = 0 |
11 |
private var _mass:Number = 0 |
12 |
private var _xSpeed:Number = 0 |
13 |
private var _ySpeed:Number = 0 |
14 |
|
15 |
public function Ball(mass:Number = 10.0, angle:Number = Math.PI, speed:Number = 10.0):void |
16 |
{
|
17 |
this.mass = mass |
18 |
this._radius = this.width/2 |
19 |
this.xSpeed = speed*Math.sin(angle) |
20 |
this.ySpeed = speed*Math.cos(angle) |
21 |
}
|
22 |
}
|
23 |
}
|
Untuk informasi lebih lanjut tentang fungsi trigonometrik Math.sin() dan Math.cos() ini, lihat Tips Cepat ini.
Langkah 3: Menyediakan Getters dan Setter
Di kelas Ball, kita menyediakan getter dan setter untuk properti.
1 |
|
2 |
public function get radius():Number |
3 |
{
|
4 |
return this._radius |
5 |
}
|
6 |
|
7 |
public function set mass(mass:Number):void |
8 |
{
|
9 |
this._mass = mass |
10 |
}
|
11 |
|
12 |
public function get mass():Number |
13 |
{
|
14 |
return this._mass |
15 |
}
|
16 |
|
17 |
public function set xSpeed(xSpeed:Number):void |
18 |
{
|
19 |
this._xSpeed = xSpeed |
20 |
}
|
21 |
|
22 |
public function get xSpeed():Number |
23 |
{
|
24 |
return this._xSpeed |
25 |
}
|
26 |
|
27 |
public function set ySpeed(ySpeed:Number):void |
28 |
{
|
29 |
this._ySpeed = ySpeed |
30 |
}
|
31 |
|
32 |
public function get ySpeed():Number |
33 |
{
|
34 |
return this._ySpeed |
35 |
}
|
Langkah 4: Fungsi Update
Fungsi ini memperbarui properti x dan y bola kita sesuai dengan _xSpeed
dan _ySpeed
. Kita akan menerapkan fungsi ini di kelas Ball
.
1 |
|
2 |
public function update():void |
3 |
{
|
4 |
this.x += _xSpeed |
5 |
this.y += _ySpeed |
6 |
}
|
Langkah 5: Kelas yang Selesai
Kita akan menyelesaikan kelas Ball
di langkah ini.
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.Stage |
5 |
import flash.display.Sprite |
6 |
import flash.events.Event |
7 |
|
8 |
public class Ball extends Sprite |
9 |
{
|
10 |
private var _radius:Number = 0 |
11 |
private var _mass:Number = 0 |
12 |
private var _xSpeed:Number = 0 |
13 |
private var _ySpeed:Number = 0 |
14 |
|
15 |
public function Ball(mass:Number = 10.0, angle:Number = Math.PI, speed:Number = 10.0):void |
16 |
{
|
17 |
this.mass = mass |
18 |
this._radius = this.width/2 |
19 |
this.xSpeed = speed*Math.sin(angle) |
20 |
this.ySpeed = speed*Math.cos(angle) |
21 |
}
|
22 |
|
23 |
public function get radius():Number |
24 |
{
|
25 |
return this._radius |
26 |
}
|
27 |
|
28 |
public function set mass(mass:Number):void |
29 |
{
|
30 |
this._mass = mass |
31 |
}
|
32 |
|
33 |
public function get mass():Number |
34 |
{
|
35 |
return this._mass |
36 |
}
|
37 |
|
38 |
public function set xSpeed(xSpeed:Number):void |
39 |
{
|
40 |
this._xSpeed = xSpeed |
41 |
}
|
42 |
|
43 |
public function get xSpeed():Number |
44 |
{
|
45 |
return this._xSpeed |
46 |
}
|
47 |
|
48 |
public function set ySpeed(ySpeed:Number):void |
49 |
{
|
50 |
this._ySpeed = ySpeed |
51 |
}
|
52 |
|
53 |
public function get ySpeed():Number |
54 |
{
|
55 |
return this._ySpeed |
56 |
}
|
57 |
|
58 |
public function update():void |
59 |
{
|
60 |
this.x += _xSpeed |
61 |
this.y += _ySpeed |
62 |
}
|
63 |
}
|
64 |
}
|
Langkah 6: Tampilkan Objek untuk Kelas Bola
Di file sumber saya menyertakan FLA awal yang berisi semua item perpustakaan yang dibutuhkan. Anda dapat menggambarnya sendiri jika Anda mau, tentu saja. Pastikan FLA Anda memiliki objek tampilan berikut:

(Ed. Note: itu salah ketik: "ennemyball" seharusnya mengatakan "ballball".)
Langkah 7: Menghubungkan Objek Perpustakaan
Kelas Ball
yang baru kita buat harus dikaitkan dengan Sprite enemyball
di perpustakaan.



playerball
Sprite harus memiliki Ball
sebagai kelas dasar dan PlayerBall
sebagai kelas.



Klip video score
harus memiliki kelas Score
.



Langkah 8: Kelas Aplikasi (Kelas Dokumen)
Kelas Application
akan berisi semua logika game. Kita mengimpor semua kelas yang dibutuhkan. Seperti yang Anda lihat, kita akan menggunakan TweenMax.
Selanjutnya kita mendefinisikan variabel bidang kita. Variabel bidang pertama adalah ballPlayer
.
Karena kelas dasar dari Sprite playerball
adalah Ball
, kita dapat menyimpan Kelas ini dalam variabel ballPlayer
. Ini membuatnya lebih mudah nanti untuk memeriksa tabrakan antara ballPlayer
dan bola musuh.
Variabel bidang kedua adalah array yang akan berisi semua bola musuh kita. Variabel ketiga adalah timer yang akan digunakan untuk melakukan loop permainan utama. Bidang keempat dan terakhir adalah turunan dari objek perpustakaan Score
kita yang akan digunakan untuk menampilkan waktu permainan yang telah berlalu. Dalam konstruktor kita memanggil fungsi init()
yang akan saya jelaskan pada langkah selanjutnya.
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.Sprite |
5 |
import flash.display.Graphics |
6 |
import flash.events.Event |
7 |
import flash.events.TimerEvent |
8 |
import flash.events.MouseEvent |
9 |
import flash.geom.Matrix |
10 |
import flash.utils.Timer |
11 |
import flash.ui.Mouse |
12 |
import com.greensock.TweenMax |
13 |
import com.greensock.easing.* |
14 |
|
15 |
public class Application extends Sprite |
16 |
{
|
17 |
|
18 |
private var ballPlayer:Ball |
19 |
private var eballs:Array |
20 |
private var tmr:Timer |
21 |
private var score:Score |
22 |
|
23 |
public function Application():void |
24 |
{
|
25 |
init() |
26 |
}
|
27 |
}
|
28 |
}
|
Jangan lupa untuk menautkan kelas dokumen!.

Langkah 9: Fungsi init()
Perhatikan kode ini:
1 |
|
2 |
private function init():void |
3 |
{
|
4 |
ballPlayer = new PlayerBall() |
5 |
eballs = new Array() |
6 |
tmr = new Timer(10) |
7 |
score = new Score() |
8 |
|
9 |
stage.align = "TL" |
10 |
stage.scaleMode = "noScale" |
11 |
Mouse.hide() |
12 |
|
13 |
setBackground() |
14 |
|
15 |
score.x = stage.stageWidth/2 |
16 |
score.y = stage.stageHeight/2 |
17 |
stage.addChild(score) |
18 |
|
19 |
stage.addEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall) |
20 |
stage.addChild(ballPlayer) |
21 |
|
22 |
tmr.addEventListener(TimerEvent.TIMER, updateTime) |
23 |
|
24 |
stage.addEventListener(MouseEvent.CLICK, startGame) |
25 |
}
|
Di empat baris pertama kita menginisialisasi variabel bidang.
Selanjutnya kita pastikan panggung kita sejajar dengan sudut kiri atas dan tidak skala.
Kita menyembunyikan kursor mouse. Kursor kita akan diganti dengan playerball
Sprite. Selanjutnya kita memanggil fungsi setBackground
(dijelaskan pada langkah berikutnya).
Kita memusatkan score
di layar dan menambahkannya ke daftar tampilan. Untuk memperbarui posisi ballPlayer
, kita lampirkan acara MouseEvent.MOUSE MOVE ke atas panggung.
Fungsi updatePlayerBall
(dijelaskan pada langkah 11) akan menangani MouseEvent ini. Selanjutnya kita menambahkan ballPlayer
ke daftar tampilan.
Timer akan digunakan untuk menampilkan waktu permainan. Kita melampirkan pendengar TimerEvent.TIMER ke timer, yang akan memicu fungsi updateTime()
(dijelaskan pada Langkah 12) setiap 10 milidetik.
Akhirnya, kita menambahkan MouseEvent. KLIK ke panggung. Fungsi startGame
(dijelaskan pada langkah 13) kemudian akan memulai permainan kita.
Langkah 10: setBackground() Fungsi
Fungsi ini menambahkan background gradien radial ke daftar tampilan. Untuk menggambar gradien pada Sprite Anda harus menentukan jenis gradien, warna yang ingin Anda gunakan, nilai alpha warna, rasio (ini menentukan distribusi warna) dan metode penyebaran.
Untuk informasi lebih lanjut, lihat Tips Cepat ini tentang gradien.
1 |
|
2 |
private function setBackground():void |
3 |
{
|
4 |
var type:String = "radial" |
5 |
var colors:Array = [0xffffff,0xcccccc] |
6 |
var alphas:Array = [ 1, 1 ] |
7 |
var ratios:Array = [ 0, 255 ] |
8 |
var matr:Matrix = new Matrix() |
9 |
matr.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI / 2, 0, 0 ) |
10 |
//SpreadMethod will define how the gradient is spread. Note!!! Flash uses CONSTANTS to represent String literals
|
11 |
var sprMethod:String = "pad" |
12 |
//Start the Gradietn and pass our variables to it
|
13 |
var sprite:Sprite = new Sprite() |
14 |
//Save typing + increase performance through local reference to a Graphics object
|
15 |
var g:Graphics = sprite.graphics |
16 |
g.beginGradientFill( type, colors, alphas, ratios, matr, sprMethod ) |
17 |
g.drawRect(0,0,stage.stageWidth,stage.stageHeight) |
18 |
|
19 |
stage.addChild(sprite) |
20 |
}
|
Langkah 11: Fungsi updatePlayerBall()
Fungsi ini memperbarui posisi ballPlayer
sesuai dengan posisi mouse Anda.
1 |
|
2 |
private function updatePlayerBall(e:MouseEvent):void |
3 |
{
|
4 |
ballPlayer.x = mouseX |
5 |
ballPlayer.y = mouseY |
6 |
}
|
Langkah 12: Fungsi updateTime()
Kita menghitung waktu dalam detik dan memasukkannya ke dalam kotak teks Sprite score
kita. Setiap 5000ms (lima detik) kita menambahkan bola baru ke permainan.
1 |
|
2 |
private function updateTime(e:TimerEvent):void |
3 |
{
|
4 |
score.txtScore.text = String(((tmr.currentCount*tmr.delay)/1000).toFixed(2)); |
5 |
if((tmr.currentCount*tmr.delay) % 5000 == 0) |
6 |
{
|
7 |
addBall(); |
8 |
}
|
9 |
}
|
Langkah 13: Fungsi startGame()
Game dimulai dengan mengklik panggung. Pertama-tama kita menghapus pendengar untuk klik panggung, sehingga kita tidak dapat memulai waktu server game. Kita menambahkan tiga bola ke permainan dengan memanggil fungsi addBall()
(dijelaskan pada langkah berikutnya) tiga kali. Kita memulai timer yang akan memperbarui waktu permainan kita.
Akhirnya kita menambahkan acara ENTER_FRAME ke stage. Fungsi gameLoop()
(dijelaskan pada Langkah 15) akan memperbarui posisi bola musuh kita.
1 |
|
2 |
private function startGame(e:MouseEvent):void |
3 |
{
|
4 |
stage.removeEventListener(MouseEvent.CLICK, startGame) |
5 |
addBall() |
6 |
addBall() |
7 |
addBall() |
8 |
tmr.start() |
9 |
stage.addEventListener(Event.ENTER_FRAME, gameLoop) |
10 |
}
|
Langkah 14: Fungsi addBall()
Pertama, kita membuat instance baru dari kelas Ball
. Kita memposisikan ball
secara acak di atas panggung dengan alfa 0 dan menambahkannya ke daftar tampilan.
Selanjutnya kita tween alpha kembali ke 1. (Saya menggunakan TweenMax, itu termasuk dalam file sumber. Anda juga dapat menggunakan mesin tween Flash built-in.) Tween kedua sebenarnya bukan tween. Itu hanya menunggu sebentar dan fungsi onComplete
mendorong ball
ke array eballs
kita. Dengan cara ini fungsi gameLoop()
(dijelaskan pada langkah berikutnya) dapat menangani sisanya.
1 |
|
2 |
private function addBall():void |
3 |
{
|
4 |
var ball:Ball = new Ball(10, Math.random()*Math.PI*2, 5) |
5 |
ball.x = Math.random()*stage.stageWidth |
6 |
ball.y = Math.random()*stage.stageHeight |
7 |
ball.alpha = 0 |
8 |
stage.addChild(ball) |
9 |
TweenMax.to(ball, 0.5, {alpha:1}) |
10 |
TweenMax.to(ball, 0, {delay: 1, onComplete:function():void{eballs.push(ball)}}) |
11 |
}
|
Langkah 15: Fungsi gameLoop()
Setiap frame akan melalui fungsi ini.
1 |
|
2 |
private function gameLoop(e:Event):void |
3 |
{
|
4 |
for (var i:uint = 0; i < eballs.length; i++) |
5 |
{
|
6 |
for (var j:uint = i + 1; j < eballs.length; j++) |
7 |
{
|
8 |
if (collision(eballs[i], eballs[j])) |
9 |
{
|
10 |
doCollision(eballs[i], eballs[j]) |
11 |
}
|
12 |
}
|
13 |
|
14 |
if(collision(eballs[i], ballPlayer)) |
15 |
{
|
16 |
endOfGame() |
17 |
break
|
18 |
}
|
19 |
|
20 |
eballs[i].update() |
21 |
checkBounds(eballs[i]) |
22 |
}
|
23 |
}
|
Kita mulai dengan mengulangi semua bola musuh kita.
Cekungan kedua untuk tabrakan antara bola musuh. Loop dimulai pada 'i + 1'. Dengan cara ini kita tidak mengecek tabrakan.
Selanjutnya kita periksa apakah ballPlayer
menyentuh bola musuh. Jika demikian, game sudah selesai. Kemudian kita memperbarui posisi bola musuh.
Kita memastikan bola tetap berada di layar game dengan memanggil fungsi checkBounds()
(dijelaskan nanti).
Langkah 16: Fungsi collision()
Fungsi ini memeriksa apakah ada pasangan bola yang bertabrakan.
Pertama kita menghitung jarak x dan jarak y antara kedua bola. Menggunakan Teorema Pythagoras (lihat diagram berikut) kita menghitung jarak absolut di antara mereka. Jika jaraknya kurang atau sama dengan jumlah jari-jari bola kita memiliki tabrakan.

1 |
|
2 |
private function collision(ball1:Ball, ball2:Ball):Boolean |
3 |
{
|
4 |
var xDist:Number = ball1.x - ball2.x |
5 |
var yDist:Number = ball1.y - ball2.y |
6 |
var Dist:Number = Math.sqrt(xDist * xDist + yDist * yDist) |
7 |
|
8 |
return Dist <= ball1.radius + ball2.radius |
9 |
}
|
Langkah 17: Funsi doCollision()
Fungsi ini akan menghitung kecepatan x dan y baru bola sesuai dengan kecepatan dan sudut tumbukan. Peringatan: matematika;)
Pertama kita menghitung jarak horizontal antara kedua bola dan kemudian jarak vertikal antara bola. Dengan jarak ini (dan lebih banyak trigonometri) kita dapat menghitung sudut antara bola (lihat diagram).

Selanjutnya kita menghitung apa yang saya sebut magnitude setiap bola. (Kita memiliki vektor xspeed dan vektor yspeed; besarnya adalah jumlah vektor dari mereka.) Kemudian kita menghitung sudut setiap bola (mirip dengan perhitungan sudut sebelumnya).
Selanjutnya kita memutar kecepatan x dan y baru dari setiap bola. Apa yang sebenarnya kita lakukan adalah memutar sistem koordinat. Dengan memutar sumbu kita, kita memiliki tabrakan 1D. (Lihat diagram berikut).

Newton mengatakan bahwa jumlah total energi kinetik dalam sistem tertutup adalah konstan. Sekarang kita menggunakan rumus ini:
v1 = (u1*(m1-m2) + 2*m2*u2)/(m1+m2)
V2 = (u2*(m2-m1) + 2*m1*u1)/(m1+m2)
dimana:
v1 = final xSpeedBall 1
v2 = final xSpeedBall 2
m1 = mass ball 1
m2 = mass ball 2
u1 = initial speed ball 1
u2 = initial speed ball 2
Kecepatan-y tidak berubah karena merupakan tabrakan 1D.
Dengan rumus ini kita bisa menghitung xSpeed
dan ySpeed
dari setiap bola.
Sekarang ketika memiliki kecepatan x dan y baru dalam sistem koordinat yang diputar. Langkah terakhir adalah mengubah semuanya kembali ke sistem koordinat normal. Kita menggunakan Math.PI/2
karena sudut antara xSpeed
dan ySpeed
harus selalu 90 derajat (pi/2 radian).
1 |
|
2 |
private function doCollision(ball1:Ball, ball2:Ball):void |
3 |
{
|
4 |
var xDist:Number = ball1.x - ball2.x |
5 |
var yDist:Number = ball1.y - ball2.y |
6 |
var collisionAngle:Number = Math.atan2(yDist, xDist) |
7 |
|
8 |
var magBall1:Number = Math.sqrt(ball1.xSpeed*ball1.xSpeed+ball1.ySpeed*ball1.ySpeed) |
9 |
var magBall2:Number = Math.sqrt(ball2.xSpeed*ball2.xSpeed+ball2.ySpeed*ball2.ySpeed) |
10 |
|
11 |
var angleBall1:Number = Math.atan2(ball1.ySpeed, ball1.xSpeed) |
12 |
var angleBall2:Number = Math.atan2(ball2.ySpeed, ball2.xSpeed) |
13 |
|
14 |
var xSpeedBall1:Number = magBall1 * Math.cos(angleBall1-collisionAngle) |
15 |
var ySpeedBall1:Number = magBall1 * Math.sin(angleBall1-collisionAngle) |
16 |
var xSpeedBall2:Number = magBall2 * Math.cos(angleBall2-collisionAngle) |
17 |
var ySpeedBall2:Number = magBall2 * Math.sin(angleBall2-collisionAngle) |
18 |
|
19 |
var finalxSpeedBall1:Number = ((ball1.mass-ball2.mass)*xSpeedBall1+(ball2.mass+ball2.mass)*xSpeedBall2)/(ball1.mass+ball2.mass) |
20 |
var finalxSpeedBall2:Number = ((ball1.mass+ball1.mass)*xSpeedBall1+(ball2.mass-ball1.mass)*xSpeedBall2)/(ball1.mass+ball2.mass) |
21 |
var finalySpeedBall1:Number = ySpeedBall1 |
22 |
var finalySpeedBall2:Number = ySpeedBall2 |
23 |
|
24 |
ball1.xSpeed = Math.cos(collisionAngle)*finalxSpeedBall1+Math.cos(collisionAngle+Math.PI/2)*finalySpeedBall1 |
25 |
ball1.ySpeed = Math.sin(collisionAngle)*finalxSpeedBall1+Math.sin(collisionAngle+Math.PI/2)*finalySpeedBall1 |
26 |
ball2.xSpeed = Math.cos(collisionAngle)*finalxSpeedBall2+Math.cos(collisionAngle+Math.PI/2)*finalySpeedBall2 |
27 |
ball2.ySpeed = Math.sin(collisionAngle)*finalxSpeedBall2+Math.sin(collisionAngle+Math.PI/2)*finalySpeedBall2 |
28 |
}
|
Untuk menemukan informasi lebih lanjut tentang tumbukan elastis, lihat di hoomanr.com.
Langkah 18: Fungsi endOfGame()
Ini dijalankan saat game berakhir.
1 |
|
2 |
private function endOfGame():void |
3 |
{
|
4 |
tmr.stop() |
5 |
Mouse.show() |
6 |
stage.removeEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall) |
7 |
stage.removeEventListener(Event.ENTER_FRAME, gameLoop) |
8 |
|
9 |
while(eballs.length > 0) |
10 |
{
|
11 |
TweenMax.to(eballs[0], 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut}) |
12 |
eballs.splice(0,1) |
13 |
}
|
14 |
|
15 |
TweenMax.to(ballPlayer, 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut}) |
16 |
}
|
Pertama-tama kita menghentikan timer. Kita menunjukkan mouse lagi. Selanjutnya kita menghapus pendengar acara MOUSE_MOVE dan ENTER_FRAME. Akhirnya kita membuat semua bola di atas panggung tidak terlihat.
Langkah 19: Fungsi checkBounds ()
Fungsi ini memastikan bola tetap berada di dalam layar game. Jadi, jika bola mengenai sisi atas atau bawah, kita membalikkan ySpeed
. jika bola menyentuh sisi kiri atau kanan layar, kita membalikkan xSpeed
. Ini menggunakan logika yang mirip dengan fungsi deteksi tabrakan bola untuk memeriksa apakah tepi bola menyentuh tepi layar.
1 |
|
2 |
private function checkBounds(ball:Ball):void |
3 |
{
|
4 |
if((ball.x + ball.radius) > stage.stageWidth) |
5 |
{
|
6 |
ball.x = stage.stageWidth - ball.radius |
7 |
ball.xSpeed *= -1 |
8 |
}
|
9 |
if((ball.x - ball.radius) < 0) |
10 |
{
|
11 |
ball.x = 0 + ball.radius |
12 |
ball.xSpeed *= -1 |
13 |
}
|
14 |
if((ball.y + ball.radius) > stage.stageHeight) |
15 |
{
|
16 |
ball.y = stage.stageHeight - ball.radius |
17 |
ball.ySpeed *= - 1 |
18 |
}
|
19 |
if((ball.y - ball.radius) < 0) |
20 |
{
|
21 |
ball.y = 0 + ball.radius |
22 |
ball.ySpeed *= - 1 |
23 |
}
|
24 |
}
|
Langkah 20: Kelas Aplikasi Lengkap
Kita telah menyelesaikan kelas Aplikasi. Kita sekarang memiliki game yang berfungsi!!!
1 |
|
2 |
package
|
3 |
{
|
4 |
import flash.display.Sprite; |
5 |
import flash.display.Graphics; |
6 |
import flash.events.Event; |
7 |
import flash.events.TimerEvent; |
8 |
import flash.events.MouseEvent; |
9 |
import flash.geom.Matrix; |
10 |
import flash.utils.Timer; |
11 |
import flash.ui.Mouse; |
12 |
import com.greensock.TweenMax; |
13 |
import com.greensock.easing.*; |
14 |
public class Application extends Sprite |
15 |
{
|
16 |
|
17 |
private var ballPlayer:Ball; |
18 |
private var eballs:Array; |
19 |
private var tmr:Timer; |
20 |
private var score:Score; |
21 |
public function Application():void |
22 |
{
|
23 |
init(); |
24 |
}
|
25 |
|
26 |
private function init():void |
27 |
{
|
28 |
ballPlayer = new PlayerBall(); |
29 |
eballs = new Array(); |
30 |
tmr = new Timer(10); |
31 |
score = new Score(); |
32 |
|
33 |
stage.align = "TL"; |
34 |
stage.scaleMode = "noScale"; |
35 |
Mouse.hide(); |
36 |
|
37 |
setBackground(); |
38 |
|
39 |
score.x = stage.stageWidth / 2; |
40 |
score.y = stage.stageHeight / 2; |
41 |
stage.addChild(score); |
42 |
|
43 |
stage.addEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall); |
44 |
stage.addChild(ballPlayer); |
45 |
|
46 |
tmr.addEventListener(TimerEvent.TIMER, updateTime); |
47 |
|
48 |
stage.addEventListener(MouseEvent.CLICK, startGame); |
49 |
}
|
50 |
|
51 |
private function setBackground():void |
52 |
{
|
53 |
var type:String = "radial"; |
54 |
var colors:Array = [0xffffff,0xcccccc]; |
55 |
var alphas:Array = [1,1]; |
56 |
var ratios:Array = [0,255]; |
57 |
var matr:Matrix = new Matrix(); |
58 |
matr.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI / 2, 0, 0 ); |
59 |
//SpreadMethod will define how the gradient is spread. Note!!! Flash uses CONSTANTS to represent String literals
|
60 |
var sprMethod:String = "pad"; |
61 |
//Start the Gradietn and pass our variables to it
|
62 |
var sprite:Sprite = new Sprite(); |
63 |
//Save typing + increase performance through local reference to a Graphics object
|
64 |
var g:Graphics = sprite.graphics; |
65 |
g.beginGradientFill( type, colors, alphas, ratios, matr, sprMethod ); |
66 |
g.drawRect(0,0,stage.stageWidth,stage.stageHeight); |
67 |
stage.addChild(sprite); |
68 |
}
|
69 |
|
70 |
private function updatePlayerBall(e:MouseEvent):void |
71 |
{
|
72 |
ballPlayer.x = mouseX; |
73 |
ballPlayer.y = mouseY; |
74 |
}
|
75 |
|
76 |
private function updateTime(e:TimerEvent):void |
77 |
{
|
78 |
score.txtScore.text = String(((tmr.currentCount*tmr.delay)/1000).toFixed(2)); |
79 |
if ((tmr.currentCount*tmr.delay) % 5000 == 0) |
80 |
{
|
81 |
addBall(); |
82 |
}
|
83 |
}
|
84 |
|
85 |
private function startGame(e:MouseEvent):void |
86 |
{
|
87 |
stage.removeEventListener(MouseEvent.CLICK, startGame); |
88 |
addBall(); |
89 |
addBall(); |
90 |
addBall(); |
91 |
tmr.start(); |
92 |
stage.addEventListener(Event.ENTER_FRAME, gameLoop); |
93 |
}
|
94 |
|
95 |
private function addBall():void |
96 |
{
|
97 |
var ball:Ball = new Ball(10,Math.random() * Math.PI * 2,5); |
98 |
ball.x = Math.random() * stage.stageWidth; |
99 |
ball.y = Math.random() * stage.stageHeight; |
100 |
ball.alpha = 0; |
101 |
stage.addChild(ball); |
102 |
TweenMax.to(ball, 0.5, {alpha:1}); |
103 |
TweenMax.to(ball, 0, {delay: 1, onComplete:function():void{eballs.push(ball)}}); |
104 |
}
|
105 |
|
106 |
private function gameLoop(e:Event):void |
107 |
{
|
108 |
for (var i:uint = 0; i < eballs.length; i++) |
109 |
{
|
110 |
for (var j:uint = i + 1; j < eballs.length; j++) |
111 |
{
|
112 |
if (collision(eballs[i],eballs[j])) |
113 |
{
|
114 |
doCollision(eballs[i], eballs[j]); |
115 |
}
|
116 |
}
|
117 |
|
118 |
if (collision(eballs[i],ballPlayer)) |
119 |
{
|
120 |
endOfGame(); |
121 |
break; |
122 |
}
|
123 |
|
124 |
eballs[i].update(); |
125 |
checkBounds(eballs[i]); |
126 |
}
|
127 |
}
|
128 |
|
129 |
private function collision(ball1:Ball, ball2:Ball):Boolean |
130 |
{
|
131 |
var xDist:Number = ball1.x - ball2.x; |
132 |
var yDist:Number = ball1.y - ball2.y; |
133 |
var Dist:Number = Math.sqrt(xDist * xDist + yDist * yDist); |
134 |
|
135 |
if (Dist <= ball1.radius + ball2.radius) |
136 |
{
|
137 |
if (ball1.x < ball2.x) |
138 |
{
|
139 |
ball1.x -= 2; |
140 |
ball2.x += 2; |
141 |
}
|
142 |
else
|
143 |
{
|
144 |
ball1.x += 2; |
145 |
ball2.x -= 2; |
146 |
}
|
147 |
|
148 |
if (ball1.y < ball2.y) |
149 |
{
|
150 |
ball1.y -= 2; |
151 |
ball2.y += 2; |
152 |
}
|
153 |
else
|
154 |
{
|
155 |
ball1.y += 2; |
156 |
ball2.y -= 2; |
157 |
}
|
158 |
}
|
159 |
|
160 |
|
161 |
return Dist <= ball1.radius + ball2.radius; |
162 |
}
|
163 |
|
164 |
private function doCollision(ball1:Ball, ball2:Ball):void |
165 |
{
|
166 |
var xDist:Number = ball1.x - ball2.x; |
167 |
var yDist:Number = ball1.y - ball2.y; |
168 |
var collisionAngle:Number = Math.atan2(yDist,xDist); |
169 |
var magBall1:Number = Math.sqrt(ball1.xSpeed * ball1.xSpeed + ball1.ySpeed * ball1.ySpeed); |
170 |
var magBall2:Number = Math.sqrt(ball2.xSpeed * ball2.xSpeed + ball2.ySpeed * ball2.ySpeed); |
171 |
var angleBall1:Number = Math.atan2(ball1.ySpeed,ball1.xSpeed); |
172 |
var angleBall2:Number = Math.atan2(ball2.ySpeed,ball2.xSpeed); |
173 |
var xSpeedBall1:Number = magBall1 * Math.cos(angleBall1 - collisionAngle); |
174 |
var ySpeedBall1:Number = magBall1 * Math.sin(angleBall1 - collisionAngle); |
175 |
var xSpeedBall2:Number = magBall2 * Math.cos(angleBall2 - collisionAngle); |
176 |
var ySpeedBall2:Number = magBall2 * Math.sin(angleBall2 - collisionAngle); |
177 |
var finalxSpeedBall1:Number = ((ball1.mass-ball2.mass)*xSpeedBall1+(ball2.mass+ball2.mass)*xSpeedBall2)/(ball1.mass+ball2.mass); |
178 |
var finalxSpeedBall2:Number = ((ball1.mass+ball1.mass)*xSpeedBall1+(ball2.mass-ball1.mass)*xSpeedBall2)/(ball1.mass+ball2.mass); |
179 |
var finalySpeedBall1:Number = ySpeedBall1; |
180 |
var finalySpeedBall2:Number = ySpeedBall2; |
181 |
ball1.xSpeed = Math.cos(collisionAngle) * finalxSpeedBall1 + Math.cos(collisionAngle + Math.PI / 2) * finalySpeedBall1; |
182 |
ball1.ySpeed = Math.sin(collisionAngle) * finalxSpeedBall1 + Math.sin(collisionAngle + Math.PI / 2) * finalySpeedBall1; |
183 |
ball2.xSpeed = Math.cos(collisionAngle) * finalxSpeedBall2 + Math.cos(collisionAngle + Math.PI / 2) * finalySpeedBall2; |
184 |
ball2.ySpeed = Math.sin(collisionAngle) * finalxSpeedBall2 + Math.sin(collisionAngle + Math.PI / 2) * finalySpeedBall2; |
185 |
}
|
186 |
|
187 |
private function endOfGame():void |
188 |
{
|
189 |
tmr.stop(); |
190 |
Mouse.show(); |
191 |
stage.removeEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall); |
192 |
stage.removeEventListener(Event.ENTER_FRAME, gameLoop); |
193 |
|
194 |
while (eballs.length > 0) |
195 |
{
|
196 |
TweenMax.to(eballs[0], 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut}); |
197 |
eballs.splice(0,1); |
198 |
}
|
199 |
|
200 |
TweenMax.to(ballPlayer, 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut}); |
201 |
}
|
202 |
|
203 |
private function checkBounds(ball:Ball):void |
204 |
{
|
205 |
if ((ball.x + ball.radius) > stage.stageWidth) |
206 |
{
|
207 |
ball.x = stage.stageWidth - ball.radius; |
208 |
ball.xSpeed *= -1; |
209 |
}
|
210 |
if ((ball.x - ball.radius) < 0) |
211 |
{
|
212 |
ball.x = 0 + ball.radius; |
213 |
ball.xSpeed *= -1; |
214 |
}
|
215 |
if ((ball.y + ball.radius) > stage.stageHeight) |
216 |
{
|
217 |
ball.y = stage.stageHeight - ball.radius; |
218 |
ball.ySpeed *= -1; |
219 |
}
|
220 |
if ((ball.y - ball.radius) < 0) |
221 |
{
|
222 |
ball.y = 0 + ball.radius; |
223 |
ball.ySpeed *= -1; |
224 |
}
|
225 |
}
|
226 |
}
|
227 |
}
|
Kesimpulan
Itu saja untuk tutorial ini. Tentu saja Anda dapat menambahkan kemungkinan untuk memulai kembali permainan, tetapi itu tidak terlalu sulit. Contoh dasar tabrakan elastis ini dapat digunakan untuk game yang lebih besar seperti game biliar atau sejenisnya.
Saya harap Anda menyukai tutorial ini, terima kasih sudah membaca!