《爆破彗星》(英語:Asteroids)是Atari公司在1979年發行的一個大型電玩遊戲。它是大型電玩黃金年代裏其中一個最受歡迎及具影響力的遊戲。《爆破彗星》使用向量圖形與二維視角。玩家需要操控一架太空飛行器去避開及以子彈摧毀飛來飛去的彗星。

Asteroids0. 本章重點1. 製作飛船2. 為每個class開一個獨立分頁3. 令火箭懂得旋轉4. 為火箭加上推進5. 加入子彈6. 加入隕石7. 讓子彈能擊碎隕石8. 加入敵方火箭9. 考考你
熟習class的用法
明白object入面可以指向另一個object
pushMatrix() ,popMatrix(), translate()和 rotate()的用法
Processing mode怎樣分頁
x1rocket = 02
3def setup():4 global rocket5 size(800,800)6 rocket = Rocket()7
8def draw():9 background(50)10 rocket.show()11 12class Rocket(object):13
14 def __init__(self):15 self.pos = PVector(width/2, height/2)16 self.vec = PVector(0,0)17 self.accel = PVector(0,-1)18 self.heading = 019
20 def show(self):21 scale = 1022 pushMatrix()23 translate(self.pos.x, self.pos.y)24 rotate(radians(self.heading))25 beginShape()26 vertex(0 *scale , 0 *scale)27 vertex(1 *scale , 0.6 *scale)28 vertex(0 *scale , -3 *scale)29 vertex(-1 *scale , 0.6 *scale)30 endShape()31 popMatrix()
開一個新的class,名字為Rocket,這艘飛船有自己的位置,速度和加速度,所以分別開3個PVector,pos, vec和accel,另外飛船有一個指向,就是船頭的方向,名叫heading。
xxxxxxxxxx121def show(self):2 scale = 103 pushMatrix()4 translate(self.pos.x, self.pos.y)5 rotate(radians(self.heading))6 beginShape()7 vertex(0 *scale , 0 *scale)8 vertex(1 *scale , 0.6 *scale)9 vertex(0 *scale , -3 *scale)10 vertex(-1 *scale , 0.6 *scale)11 endShape()12 popMatrix()在class Rocket中,pushMatrix()和popMatrix()是用來告訴程式,兩者之間的所有translate()和rotate()都是暫時的。Python Mode for Processing在繪圖圖形時,有兩種方法,一是直接指定在某座標點繪圖某圖形,好處是簡單直覺,但如果好像今次般,除了基本的偏移(translate)外,還要加上旋轉角度的話,計算座標就會十分麻煩,涉及到旋轉矩陣,所以今次我們用translate()和rotate()的方法,將整個座標系統的原點改變,再用vertex()繪劃火箭形將的箭頭。
asteroids.pyde:
xxxxxxxxxx121from rocket import *2
3rocket = 04
5def setup():6 global rocket7 size(800,800)8 rocket = Rocket()9
10def draw():11 background(50)12 rocket.show()rocket.py:
xxxxxxxxxx201class Rocket(object):2
3 def __init__(self):4 self.pos = PVector(width/2, height/2)5 self.vec = PVector(0,0)6 self.accel = PVector(0,-1)7 self.heading = 08
9 def show(self):10 scale = 1011 pushMatrix()12 translate(self.pos.x, self.pos.y)13 rotate(radians(self.heading))14 beginShape()15 vertex(0 *scale , 0 *scale)16 vertex(1 *scale , 0.6 *scale)17 vertex(0 *scale , -3 *scale)18 vertex(-1 *scale , 0.6 *scale)19 endShape()20 popMatrix()

為方便管理整個程式,一般工程師都會習慣將相關的內容放在同一個分頁,每個分頁通常都不會太長。

按下New Tab,或用Ctrl+Shift+N開一個分頁名叫rocket.py,將原本Rocket這個class的所有內容都剪下貼到這個分頁。(注意⚠⚠⚠⚠⚠: rocket.py的名字不能完全與Rocket這個class相同,我是大小字不同的,否則會有error)
之後在主頁面最上加上一句from rocket import *,這裡的rocket是小寫的,即代表著rocket.py 這個檔案。
asteroids.pyde:
xxxxxxxxxx191from rocket import *2
3rocket = 04
5def setup():6 global rocket;7 size(800,800)8 rocket = Rocket()9
10def draw():11 background(50)12
13 if (keyPressed and key == CODED):14 if (keyCode == LEFT):15 rocket.rotateRocket(-5)16 if (keyCode == RIGHT):17 rocket.rotateRocket(5)18
19 rocket.show()rocket.py:
xxxxxxxxxx241class Rocket(object):2
3 def __init__(self):4 self.pos = PVector(width/2, height/2)5 self.vec = PVector(0,0)6 self.accel = PVector(0,-1)7 self.heading = 0 #unit in degrees8
9 def show(self):10 scale = 1011 pushMatrix()12 translate(self.pos.x, self.pos.y)13 rotate(radians(self.heading))14 beginShape()15 vertex(0 *scale , 0 *scale)16 vertex(1 *scale , 0.6 *scale)17 vertex(0 *scale , -3 *scale)18 vertex(-1 *scale , 0.6 *scale)19 endShape()20 popMatrix()21
22 def rotateRocket(self, angle):23 self.heading += angle24 self.accel.rotate(radians(angle))
xxxxxxxxxx31def rotateRocket(self, angle):2 self.heading += angle3 self.accel.rotate(radians(angle))在rocket.py中,加入rotateRocket(self, angle)的函數,每次當玩家按下左方向鍵或右方向鍵時,就將heading(即船指著的方向)累加,而同時也要將加速度accel的向量旋轉。
xxxxxxxxxx101def draw():2 background(50)3
4 if (keyPressed and key == CODED):5 if (keyCode == LEFT):6 rocket.rotateRocket(-5)7 if (keyCode == RIGHT):8 rocket.rotateRocket(5)9
10 rocket.show()返回主頁,在draw()中,加入按下方向鍵的控制項。keyPressed()可以作為一個外置的event,當按下按鍵時先中斷主程式執行再返回,或者作為一個boolean變數,當用家按下按鍵時就會變為True,而這時就可以讀取鍵盤的內容。由於方向鍵並非像一般字母般係輕易表達,所以processing中將四個方向鍵(UP, DOWN, LEFT, RIGHT),還有ALT, CONTROL, SHIFT都列入特別類型keyCode。
asteroids.pyde:
xxxxxxxxxx251from rocket import *2
3rocket = 04
5def setup():6 global rocket;7 size(800,800)8 frameRate(60)9 rocket = Rocket()10
11def draw():12 background(50)13 14 if (keyPressed and key == CODED):15 if (keyCode == LEFT):16 rocket.rotateRocket(-5)17 if (keyCode == RIGHT):18 rocket.rotateRocket(5)19 if (keyCode == UP):20 rocket.propulsion()21 else:22 rocket.isPropulsion = False23
24 rocket.update()25 rocket.show()rocket.py:
xxxxxxxxxx491class Rocket(object):2
3 def __init__(self):4 self.pos = PVector(width/2, height/2)5 self.vec = PVector(0,0)6 self.accel = PVector(0,-1)7 self.heading = 0 #unit in degrees8 self.isPropulsion = False9
10 def show(self):11 if self.isPropulsion == True:12 fill('#FF0000')13 else:14 fill('#FFFFFF')15 scale = 1016 pushMatrix()17 translate(self.pos.x, self.pos.y)18 rotate(radians(self.heading))19 beginShape()20 vertex(0 *scale , 0 *scale)21 vertex(1 *scale , 0.6 *scale)22 vertex(0 *scale , -3 *scale)23 vertex(-1 *scale , 0.6 *scale)24 endShape()25 popMatrix()26
27 def rotateRocket(self, angle):28 self.heading += angle29 self.accel.rotate(radians(angle))30
31 def propulsion(self):32 self.isPropulsion = True33 self.accel.setMag(0.1)34 self.vec = PVector.add(self.vec, self.accel)35 self.vec.limit(6)36 37 def update(self):38 if self.isPropulsion == False:39 self.vec = PVector.mult(self.vec, 0.99)40
41 self.pos = PVector.add(self.pos, self.vec)42 if (self.pos.x > width):43 self.pos.x = 044 if (self.pos.x < 0):45 self.pos.x = width46 if (self.pos.y > height):47 self.pos.y = 048 if (self.pos.y < 0):49 self.pos.y = height 
在rocket.py的rocketclass中,加入propulsion(),就是推進的意思。這裡模擬在太空中,火箭要在尾部點火推進前進。
xxxxxxxxxx51def propulsion(self):2 self.isPropulsion = True3 self.accel.setMag(0.1)4 self.vec = PVector.add(self.vec, self.accel)5 self.vec.limit(6)self.accel.setMag(0.1)就是將加速度向量的量設定為0.1,再將速度向量累加,但速度如果一直累加的話,太大很難控制,為顧及遊戲性,限制速度向量的量為6
xxxxxxxxxx131def update(self):2 if self.isPropulsion == False:3 self.vec = PVector.mult(self.vec, 0.99)4
5 self.pos = PVector.add(self.pos, self.vec)6 if (self.pos.x > width):7 self.pos.x = 08 if (self.pos.x < 0):9 self.pos.x = width10 if (self.pos.y > height):11 self.pos.y = 012 if (self.pos.y < 0):13 self.pos.y = height 跟之前所有物理模擬一樣,我們為程式加入update()函數,每一個影格為其位置作更新。如果火箭超出了螢幕,就令火箭在另一邊出現。為增加遊戲可玩性,為火箭加入點阻力,令火箭不在推進的話就會慢下來。
xxxxxxxxxx161def show(self):2 if self.isPropulsion == True:3 fill('#FF0000')4 else:5 fill('#FFFFFF')6 scale = 107 pushMatrix()8 translate(self.pos.x, self.pos.y)9 rotate(radians(self.heading))10 beginShape()11 vertex(0 *scale , 0 *scale)12 vertex(1 *scale , 0.6 *scale)13 vertex(0 *scale , -3 *scale)14 vertex(-1 *scale , 0.6 *scale)15 endShape()16 popMatrix()在rocketclass的show()中,加入顏色作推進識別。如果推進中的話,火箭就但是紅色,否則就會變回白色。
xxxxxxxxxx151def draw():2 background(50)3 4 if (keyPressed and key == CODED):5 if (keyCode == LEFT):6 rocket.rotateRocket(-5)7 if (keyCode == RIGHT):8 rocket.rotateRocket(5)9 if (keyCode == UP):10 rocket.propulsion()11 else:12 rocket.isPropulsion = False13
14 rocket.update()15 rocket.show()返回主頁的draw()中,在方向鍵中加入上方向鍵UP,當按下時火箭就會向前推進。在show()前記得要先update()。
asteroids.pyde:
xxxxxxxxxx381from rocket import *2from bullet import *3
4rocket = 05bullets = []6
7def setup():8 global rocket;9 size(800,800)10 frameRate(60)11 rocket = Rocket()12 bullets = []13
14def draw():15 background(50)16 17 if (keyPressed and key == CODED):18 if (keyCode == LEFT):19 rocket.rotateRocket(-5)20 if (keyCode == RIGHT):21 rocket.rotateRocket(5)22 if (keyCode == UP):23 rocket.propulsion() 24 else:25 rocket.isPropulsion = False26
27 rocket.update()28 rocket.show()29
30 for b in bullets:31 b.update()32 b.show()33 if (b.pos.x > width or b.pos.x < 0 or b.pos.y > height or b.pos.y <0):34 bullets.remove(b)35
36def keyPressed():37 if (key == ' '):38 bullets.append(Bullet(rocket.pos, rocket.accel))rocket.py
xxxxxxxxxx491class Rocket(object):2
3 def __init__(self):4 self.pos = PVector(width/2, height/2)5 self.vec = PVector(0,0)6 self.accel = PVector(0,-1)7 self.heading = 0 #unit in degrees8 self.isPropulsion = False9
10 def show(self):11 if self.isPropulsion == True:12 fill('#FF0000')13 else:14 fill('#FFFFFF')15 scale = 1016 pushMatrix()17 translate(self.pos.x, self.pos.y)18 rotate(radians(self.heading))19 beginShape()20 vertex(0 *scale , 0 *scale)21 vertex(1 *scale , 0.6 *scale)22 vertex(0 *scale , -3 *scale)23 vertex(-1 *scale , 0.6 *scale)24 endShape()25 popMatrix()26
27 def rotateRocket(self, angle):28 self.heading += angle29 self.accel.rotate(radians(angle))30
31 def propulsion(self):32 self.isPropulsion = True33 self.accel.setMag(0.1)34 self.vec = PVector.add(self.vec, self.accel)35 self.vec.limit(6)36 37 def update(self):38 if self.isPropulsion == False:39 self.vec = PVector.mult(self.vec, 0.99)40
41 self.pos = PVector.add(self.pos, self.vec)42 if (self.pos.x > width):43 self.pos.x = 044 if (self.pos.x < 0):45 self.pos.x = width46 if (self.pos.y > height):47 self.pos.y = 048 if (self.pos.y < 0):49 self.pos.y = height bullet.py
xxxxxxxxxx121class Bullet(object):2
3 def __init__(self, _pos, _vec):4 self.pos = _pos5 self.vec = _vec.copy().setMag(4)6
7 def update(self):8 self.pos = PVector.add(self.pos, self.vec)9
10 def show(self):11 fill('#FFFF00')12 ellipse(self.pos.x, self.pos.y, 5, 5)
開一個新的分頁叫bullet.py,定義一個class叫做Bullet,用來管理有關子彈的變數和函數。
xxxxxxxxxx121class Bullet(object):2
3 def __init__(self, _pos, _vec):4 self.pos = _pos5 self.vec = _vec.copy().setMag(4)6
7 def update(self):8 self.pos = PVector.add(self.pos, self.vec)9
10 def show(self):11 fill('#FFFF00')12 ellipse(self.pos.x, self.pos.y, 5, 5)子彈的速度要跟發射子彈時火箭的方向一致,但只需要知道方向就可以,我們固定它的速度為4,所以首先要用self.vec = _vec.copy().setMag(4)將輸入的速度向量的長度設定成4個單位。
xxxxxxxxxx51from rocket import *2from bullet import *3
4rocket = 05bullets = []返回主頁,在最上面加入bullet.py分頁的內容,之後開一個空的陣列叫bullets用來裝起全部子彈。
xxxxxxxxxx31def keyPressed():2 if (key == ' '):3 bullets.append(Bullet(rocket.pos, rocket.accel))在setup()和draw()之外,加入keyPressed()event,今次不在繼續在draw()中加入子彈,原因是draw()的更新率太快,按一下鍵就已經產生了十多發子彈。當按下空白鍵,就新增一顆子彈,子彈的位置就是火箭當前的位置,而子彈的速度,是火箭當前的指向,你可以用heading變數去指定子彈速度的方向,或直接使用火箭的加速度,這個加速度的方向應該是與heading一致的。
xxxxxxxxxx51for b in bullets:2 b.update()3 b.show()4 if (b.pos.x > width or b.pos.x < 0 or b.pos.y > height or b.pos.y <0):5 bullets.remove(b)在主程式最下加入,用來更新和顯示所有子彈,如果子彈超出了畫面,則將子彈從陣列中移除,以免佔用大量內存。
asteroids.pyde:
xxxxxxxxxx491from rocket import *2from bullet import *3from rock import *4
5rocket = 06bullets = []7rocks = []8
9def setup():10 global rocket, bullets, rocks;11 size(800,800)12 frameRate(60)13 rocket = Rocket()14 bullets = []15 rocks = []16 for i in range(3):17 rocks.append(Rock(3))18 for i in range(2):19 rocks.append(Rock(2))20
21def draw():22 background(50)23 24 if (keyPressed and key == CODED):25 if (keyCode == LEFT):26 rocket.rotateRocket(-5)27 if (keyCode == RIGHT):28 rocket.rotateRocket(5)29 if (keyCode == UP):30 rocket.propulsion() 31 else:32 rocket.isPropulsion = False33
34 rocket.update()35 rocket.show()36
37 for b in bullets:38 b.update()39 b.show()40 if (b.pos.x > width or b.pos.x < 0 or b.pos.y > height or b.pos.y <0):41 bullets.remove(b)42 43 for r in rocks:44 r.update()45 r.show()46
47def keyPressed():48 if (key == ' '):49 bullets.append(Bullet(rocket.pos, rocket.accel))rock.py
xxxxxxxxxx371class Rock(object):2
3 def __init__(self, _level):4 self.pos = PVector(random(0, width), random(0, height))5 self.vec = PVector.random2D()6 self.level = _level7 self.angle = random(0, TWO_PI)8
9 def update(self):10 self.pos = PVector.add(self.pos, self.vec)11 if (self.pos.x > width):12 self.pos.x = 013 if (self.pos.x < 0):14 self.pos.x = width15 if (self.pos.y > height):16 self.pos.y = 017 if (self.pos.y < 0):18 self.pos.y = height 19 self.angle += 0.0120
21 def show(self):22 scale = pow(3, self.level)23 fill('#CBD3FF')24 pushMatrix()25 translate(self.pos.x, self.pos.y)26 rotate(self.angle)27 beginShape()28 vertex(-2 *scale, scale * -1)29 vertex(-.5 *scale, scale * 0)30 vertex(-2 *scale, scale * 1)31 vertex(-1 *scale, scale * 2)32 vertex(1 *scale, scale * 2)33 vertex(2 *scale, scale * 0)34 vertex(1 *scale, scale * -2)35 vertex(-1 *scale, scale * -2)36 endShape()37 popMatrix()bullet.py 和rocket.py沒有修改,就不貼出來了。

加入一個叫rock的class,本質上跟其他的class差不多,都是初始化時決定位置和速度,為了其顯示上多點動態,所以也加入了angle旋轉角度的變化。
比較特別的是level變數,由於原遊戲有幾款不同大小的隕石,當子彈打碎了大隕石後就會變成細隕石,所以要加入level變數,數字越大代表size越大。
返回主程式頁。
xxxxxxxxxx71from rocket import *2from bullet import *3from rock import *4
5rocket = 06bullets = []7rocks = []在最上匯入rock.py的內容,加入一個空的陣列名叫rocks。
xxxxxxxxxx111def setup():2 global rocket, bullets, rocks;3 size(800,800)4 frameRate(60)5 rocket = Rocket()6 bullets = []7 rocks = []8 for i in range(3):9 rocks.append(Rock(3))10 for i in range(2):11 rocks.append(Rock(2))在setup()中,先清空rocks的內容變成空陣列,接著加入 3 顆level 3 大小和 2 顆 level 2 大小的隕石。
xxxxxxxxxx31for r in rocks:2 r.update()3 r.show()在draw()的最後,更新和顯示全部隕石。
asteroids.pyde:
xxxxxxxxxx611from rocket import *2from bullet import *3from rock import *4
5rocket = 06bullets = []7rocks = []8
9def setup():10 global rocket, bullets, rocks;11 size(800,800)12 frameRate(60)13 rocket = Rocket()14 bullets = []15 rocks = []16 for i in range(3):17 rocks.append(Rock(3))18 for i in range(2):19 rocks.append(Rock(2))20
21def draw():22 background(50)23 24 if (keyPressed and key == CODED):25 if (keyCode == LEFT):26 rocket.rotateRocket(-5)27 if (keyCode == RIGHT):28 rocket.rotateRocket(5)29 if (keyCode == UP):30 rocket.propulsion() 31 else:32 rocket.isPropulsion = False33
34 rocket.update()35 rocket.show()36
37 for b in bullets:38 b.update()39 b.show()40 if (b.pos.x > width or b.pos.x < 0 or b.pos.y > height or b.pos.y <0):41 bullets.remove(b)42 43 for r in rocks:44 for b in bullets:45 if (PVector.dist(b.pos, r.pos) < pow(3, r.level)*2):46 if (r.level > 1):47 for i in range(2):48 temp = Rock(r.level-1)49 temp.pos = r.pos50 rocks.append(temp)51 bullets.remove(b)52 rocks.remove(r)53 break #跳出迴圈, 不用運算全部子彈54 55 for r in rocks:56 r.update()57 r.show()58 59def keyPressed():60 if (key == ' '):61 bullets.append(Bullet(rocket.pos, rocket.accel))bullet.py、rock.py 和rocket.py沒有修改,就不貼出來了。

在主程式頁面的draw()中,加入每粒隕石對每發子彈的計算。
xxxxxxxxxx111for r in rocks:2 for b in bullets:3 if (PVector.dist(b.pos, r.pos) < pow(3, r.level)*2):4 if (r.level > 1):5 for i in range(2):6 temp = Rock(r.level-1)7 temp.pos = r.pos8 rocks.append(temp)9 bullets.remove(b)10 rocks.remove(r)11 break #跳出迴圈, 不用運算全部子彈由於隕石的大小是以其基礎vertex座標再乘以系數vertex座標的最大值為2,所以我簡單當成隕石是一個半徑為
隕石每打碎一次後,就會分裂成兩顆細一個level的的小隕石,直到level 1的隕石被擊碎就不再分裂。所以每打中一次,就給一個for i in range(2):的for迴圈,temp = Rock(r.level-1)重新生成一顆size為level-1的隕石,temp.pos = r.pos指定temp隕石的位置為原本被打中的隕石位置,rocks.append(temp)新增到隕石陣列中,之後就將原本的這塊大隕石和子彈都移除。最後, break是跳出for b in bullets:的for迴圈,不用再計算餘下的子彈,可以直接計算下一塊隕石。
asteroids.pyde
xxxxxxxxxx801from rocket import *2from bullet import *3from rock import *4
5rocket = 06bullets = []7rocks = []8enemy = 09timer = 010timer1 = 011
12def setup():13 global rocket, bullets, rocks, enemy;14 size(800,800)15 frameRate(60)16 rocket = Rocket()17 bullets = []18 rocks = []19 enemy = Rocket()20
21 for i in range(3):22 rocks.append(Rock(3))23 for i in range(2):24 rocks.append(Rock(2))25
26def draw():27 global timer, timer128
29 background(50)30 31 if (keyPressed and key == CODED):32 if (keyCode == LEFT):33 rocket.rotateRocket(-5)34 if (keyCode == RIGHT):35 rocket.rotateRocket(5)36 if (keyCode == UP):37 rocket.propulsion() 38 else:39 rocket.isPropulsion = False40
41 rocket.update()42 rocket.show()43
44 for b in bullets:45 b.update()46 b.show()47 if (b.pos.x > width or b.pos.x < 0 or b.pos.y > height or b.pos.y <0):48 bullets.remove(b)49 50 for r in rocks:51 for b in bullets:52 if (PVector.dist(b.pos, r.pos) < pow(3, r.level)*2):53 if (r.level > 1):54 for i in range(2):55 temp = Rock(r.level-1)56 temp.pos = r.pos57 rocks.append(temp)58 bullets.remove(b)59 rocks.remove(r)60 break #跳出迴圈, 不用運算全部子彈61 62 for r in rocks:63 r.update()64 r.show()65 66 enemy2rocketAngle = PVector.sub(rocket.pos, enemy.pos).heading()67 enemy.accel = PVector.fromAngle(enemy2rocketAngle).setMag(0.1)68 enemy.heading = degrees(enemy2rocketAngle)+9069 if (millis() - timer1 >= 150):70 enemy.propulsion()71 timer1 = millis()72 if (millis() - timer >= 500):73 bullets.append(Bullet(enemy.pos, enemy.accel))74 timer = millis()75 enemy.update()76 enemy.show()77
78def keyPressed():79 if (key == ' '):80 bullets.append(Bullet(rocket.pos, rocket.accel))其他版面沒有變,所以就不再貼出來。

xxxxxxxxxx61rocket = 02bullets = []3rocks = []4enemy = 05timer = 06timer1 = 0在一開始的宣告區,加入enemy敵人的火箭,之後宣告多兩個時間的變數一會兒用。
xxxxxxxxxx131def setup():2 global rocket, bullets, rocks, enemy;3 size(800,800)4 frameRate(60)5 rocket = Rocket()6 bullets = []7 rocks = []8 enemy = Rocket()9
10 for i in range(3):11 rocks.append(Rock(3))12 for i in range(2):13 rocks.append(Rock(2))在setup()中宣告enemy,記得要先將enemy變成宣告成global。
xxxxxxxxxx111enemy2rocketAngle = PVector.sub(rocket.pos, enemy.pos).heading()2enemy.accel = PVector.fromAngle(enemy2rocketAngle).setMag(0.1)3enemy.heading = degrees(enemy2rocketAngle)+904if (millis() - timer1 >= 150):5 enemy.propulsion()6 timer1 = millis()7if (millis() - timer >= 500):8 bullets.append(Bullet(enemy.pos, enemy.accel))9 timer = millis()10enemy.update()11enemy.show()在draw()的最後,更新一下enemy的行為。首先,敵船永遠指著我方的火箭,用向量運算就十分簡單,只要將再個位置向量相減就是方向向量(由於processing中y軸是跟數學中的y軸正負極相反的,所以向量相減的次序跟數學是相反的)。
接著用PVector.fromAngle(enemy2rocketAngle).setMag(0.1),用一個指定的角度建立一個新的向量,今次則是指定用剛剛找到的兩者間的角度來建立,並設定其值為0.1。
enemy.heading = degrees(enemy2rocketAngle)+90,rocketclass的heading變數一直都是用degree來表達而非radian,單位小心不要錯,所以要先轉成degrees,再加90度的原因也是因為processing的角度計算,0度是指向天空(即一般數學的y軸方向),但PVector中的角度卻跟數學一樣是由x軸計起的,所以要再加上90度。
xxxxxxxxxx31if (millis() - timer >= 500):2 //do somethings3 timer = millis()是一個常見的用法,取代delay()等線式的延時方法,上述500即每500毫秒執行中間的命令一次。
最後就是更新和顯示。
遊戲到這個階段都沒有game over的。原遊戲的設定是有3條生命的,火箭被隕石或子彈打中後就會失去一條生命,你可以將火箭被打中或擊中後,將其位置放回畫面中間,速度歸零。