《爆破彗星》(英語:Asteroids)是Atari公司在1979年發行的一個大型電玩遊戲。它是大型電玩黃金年代裏其中一個最受歡迎及具影響力的遊戲。《爆破彗星》使用向量圖形與二維視角。玩家需要操控一架太空飛行器去避開及以子彈摧毀飛來飛去的彗星。
Asteroids0. 本章重點1. 製作飛船2. 為每個class開一個獨立分頁3. 令火箭懂得旋轉4. 為火箭加上推進5. 加入子彈6. 加入隕石7. 讓子彈能擊碎隕石8. 加入敵方火箭9. 考考你
熟習class的用法
明白object入面可以指向另一個object
pushMatrix()
,popMatrix()
, translate()
和 rotate()
的用法
Processing mode怎樣分頁
x1rocket = 0
2
3def setup():
4 global rocket
5 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 = 0
19
20 def show(self):
21 scale = 10
22 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
。
xxxxxxxxxx
121def show(self):
2 scale = 10
3 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
:
xxxxxxxxxx
121from rocket import *
2
3rocket = 0
4
5def setup():
6 global rocket
7 size(800,800)
8 rocket = Rocket()
9
10def draw():
11 background(50)
12 rocket.show()
rocket.py
:
xxxxxxxxxx
201class 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
8
9 def show(self):
10 scale = 10
11 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
:
xxxxxxxxxx
191from rocket import *
2
3rocket = 0
4
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
:
xxxxxxxxxx
241class 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 degrees
8
9 def show(self):
10 scale = 10
11 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 += angle
24 self.accel.rotate(radians(angle))
xxxxxxxxxx
31def rotateRocket(self, angle):
2 self.heading += angle
3 self.accel.rotate(radians(angle))
在rocket.py
中,加入rotateRocket(self, angle)
的函數,每次當玩家按下左方向鍵或右方向鍵時,就將heading
(即船指著的方向)累加,而同時也要將加速度accel
的向量旋轉。
xxxxxxxxxx
101def 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
:
xxxxxxxxxx
251from rocket import *
2
3rocket = 0
4
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 = False
23
24 rocket.update()
25 rocket.show()
rocket.py
:
xxxxxxxxxx
491class 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 degrees
8 self.isPropulsion = False
9
10 def show(self):
11 if self.isPropulsion == True:
12 fill('#FF0000')
13 else:
14 fill('#FFFFFF')
15 scale = 10
16 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 += angle
29 self.accel.rotate(radians(angle))
30
31 def propulsion(self):
32 self.isPropulsion = True
33 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 = 0
44 if (self.pos.x < 0):
45 self.pos.x = width
46 if (self.pos.y > height):
47 self.pos.y = 0
48 if (self.pos.y < 0):
49 self.pos.y = height
在rocket.py
的rocket
class中,加入propulsion()
,就是推進的意思。這裡模擬在太空中,火箭要在尾部點火推進前進。
xxxxxxxxxx
51def propulsion(self):
2 self.isPropulsion = True
3 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
xxxxxxxxxx
131def 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 = 0
8 if (self.pos.x < 0):
9 self.pos.x = width
10 if (self.pos.y > height):
11 self.pos.y = 0
12 if (self.pos.y < 0):
13 self.pos.y = height
跟之前所有物理模擬一樣,我們為程式加入update()
函數,每一個影格為其位置作更新。如果火箭超出了螢幕,就令火箭在另一邊出現。為增加遊戲可玩性,為火箭加入點阻力,令火箭不在推進的話就會慢下來。
xxxxxxxxxx
161def show(self):
2 if self.isPropulsion == True:
3 fill('#FF0000')
4 else:
5 fill('#FFFFFF')
6 scale = 10
7 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()
在rocket
class的show()
中,加入顏色作推進識別。如果推進中的話,火箭就但是紅色,否則就會變回白色。
xxxxxxxxxx
151def 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 = False
13
14 rocket.update()
15 rocket.show()
返回主頁的draw()
中,在方向鍵中加入上方向鍵UP
,當按下時火箭就會向前推進。在show()
前記得要先update()
。
asteroids.pyde
:
xxxxxxxxxx
381from rocket import *
2from bullet import *
3
4rocket = 0
5bullets = []
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 = False
26
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
xxxxxxxxxx
491class 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 degrees
8 self.isPropulsion = False
9
10 def show(self):
11 if self.isPropulsion == True:
12 fill('#FF0000')
13 else:
14 fill('#FFFFFF')
15 scale = 10
16 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 += angle
29 self.accel.rotate(radians(angle))
30
31 def propulsion(self):
32 self.isPropulsion = True
33 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 = 0
44 if (self.pos.x < 0):
45 self.pos.x = width
46 if (self.pos.y > height):
47 self.pos.y = 0
48 if (self.pos.y < 0):
49 self.pos.y = height
bullet.py
xxxxxxxxxx
121class Bullet(object):
2
3 def __init__(self, _pos, _vec):
4 self.pos = _pos
5 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
,用來管理有關子彈的變數和函數。
xxxxxxxxxx
121class Bullet(object):
2
3 def __init__(self, _pos, _vec):
4 self.pos = _pos
5 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個單位。
xxxxxxxxxx
51from rocket import *
2from bullet import *
3
4rocket = 0
5bullets = []
返回主頁,在最上面加入bullet.py
分頁的內容,之後開一個空的陣列叫bullets
用來裝起全部子彈。
xxxxxxxxxx
31def keyPressed():
2 if (key == ' '):
3 bullets.append(Bullet(rocket.pos, rocket.accel))
在setup()
和draw()
之外,加入keyPressed()
event,今次不在繼續在draw()
中加入子彈,原因是draw()
的更新率太快,按一下鍵就已經產生了十多發子彈。當按下空白鍵,就新增一顆子彈,子彈的位置就是火箭當前的位置,而子彈的速度,是火箭當前的指向,你可以用heading
變數去指定子彈速度的方向,或直接使用火箭的加速度,這個加速度的方向應該是與heading一致的。
xxxxxxxxxx
51for 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
:
xxxxxxxxxx
491from rocket import *
2from bullet import *
3from rock import *
4
5rocket = 0
6bullets = []
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 = False
33
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
xxxxxxxxxx
371class 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 = _level
7 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 = 0
13 if (self.pos.x < 0):
14 self.pos.x = width
15 if (self.pos.y > height):
16 self.pos.y = 0
17 if (self.pos.y < 0):
18 self.pos.y = height
19 self.angle += 0.01
20
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
越大。
返回主程式頁。
xxxxxxxxxx
71from rocket import *
2from bullet import *
3from rock import *
4
5rocket = 0
6bullets = []
7rocks = []
在最上匯入rock.py
的內容,加入一個空的陣列名叫rocks
。
xxxxxxxxxx
111def 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 大小的隕石。
xxxxxxxxxx
31for r in rocks:
2 r.update()
3 r.show()
在draw()
的最後,更新和顯示全部隕石。
asteroids.pyde
:
xxxxxxxxxx
611from rocket import *
2from bullet import *
3from rock import *
4
5rocket = 0
6bullets = []
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 = False
33
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.pos
50 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()
中,加入每粒隕石對每發子彈的計算。
xxxxxxxxxx
111for 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.pos
8 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
xxxxxxxxxx
801from rocket import *
2from bullet import *
3from rock import *
4
5rocket = 0
6bullets = []
7rocks = []
8enemy = 0
9timer = 0
10timer1 = 0
11
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, timer1
28
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 = False
40
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.pos
57 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)+90
69 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))
其他版面沒有變,所以就不再貼出來。
xxxxxxxxxx
61rocket = 0
2bullets = []
3rocks = []
4enemy = 0
5timer = 0
6timer1 = 0
在一開始的宣告區,加入enemy
敵人的火箭,之後宣告多兩個時間的變數一會兒用。
xxxxxxxxxx
131def 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
。
xxxxxxxxxx
111enemy2rocketAngle = PVector.sub(rocket.pos, enemy.pos).heading()
2enemy.accel = PVector.fromAngle(enemy2rocketAngle).setMag(0.1)
3enemy.heading = degrees(enemy2rocketAngle)+90
4if (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
,rocket
class的heading
變數一直都是用degree來表達而非radian,單位小心不要錯,所以要先轉成degrees,再加90度的原因也是因為processing的角度計算,0度是指向天空(即一般數學的y軸方向),但PVector
中的角度卻跟數學一樣是由x軸計起的,所以要再加上90度。
xxxxxxxxxx
31if (millis() - timer >= 500):
2 //do somethings
3 timer = millis()
是一個常見的用法,取代delay()
等線式的延時方法,上述500
即每500毫秒執行中間的命令一次。
最後就是更新和顯示。
遊戲到這個階段都沒有game over的。原遊戲的設定是有3條生命的,火箭被隕石或子彈打中後就會失去一條生命,你可以將火箭被打中或擊中後,將其位置放回畫面中間,速度歸零。