在Example 1.2中,我們利用向量來寫模擬物體運動的程式。在程式中,我們先算出球的位置,然後在那個位置畫出球:
position += velocity
pygame.draw.circle(screen, BLACK, position, RADIUS)
這就是模擬物體運動的最基本做法,這個做法,可以歸納為如下的演算法:
Motion 101
1. 將位置向量加上速度以得到新的位置向量
2. 在新位置畫出物體
題外話。看到101這個數字,大概可以猜到代表的意思。不過,實在很好奇,為什麼是101,不是100或123?網上搜尋了一下,在維基百科:101 (number)找到了答案。
言歸正傳。在那個例子中,是把Motion 101相關的程式直接寫在主程式中,但這並不是個好做法。比較好的做法,是把這部分的程式封裝在一個類別中,以發揮物件導向設計的優勢。接下來,就來設計一個叫做Mover
的類別,把這部分的程式封裝在其中。
在設計Mover
這個類別時,必須考慮兩個問題:
Mover
的物件裡頭包含了哪些資料?Mover
的物件具有哪些功能?這兩個問題的答案就在Motion 101這個演算法中。
一個Mover
的物件,應該含有兩份資料:位置及速度,也就是position
和velocity
這兩個向量。至於它應具備的功能,其實也很簡單,就是移動及現身被看見。我們會把移動相關的程式碼寫在update()
這個方法中;而現身被看見,其實也就是在畫面上畫出物件樣貌,這部分的程式碼,則寫在show()
這個方法中。程式碼如下:
def update(self):
self.position += self.velocity
def show(self):
pygame.draw.circle(self.screen, (0, 0, 0), self.position, 24)
在建立Mover
的物件時,需要先設定,也就是初始化(initialize)的東西,如起始位置、初始速度等,就寫在__init__()
中:
def __init__(self):
# 取得顯示畫面的大小
self.screen = pygame.display.get_surface()
self.width, self.height = self.screen.get_size()
# 設定物件的起始位置
x = random.uniform(0, self.width)
y = random.uniform(0, self.height)
self.position = pygame.Vector2(x, y)
# 設定物件的初始速度
vx = random.uniform(-2, 2)
vy = random.uniform(-2, 2)
self.velocity = pygame.Vector2(vx, vy)
最後,我們要決定物件碰到邊界時,會怎麼反應。假設我們希望碰到邊界時,物件會繞一圈從另一邊跑出來,程式的寫法如下:
def check_edges(self):
if self.position.x > self.width:
self.position.x = 0
elif self.position.x < 0:
self.position.x = self.width
if self.position.y > self.height:
self.position.y = 0
elif self.position.y < 0:
self.position.y = self.height
這樣就完成了Mover
這個類別的設計。
下面的例子,就是使用Mover
這個類別產生一個物件mover
,然後讓這個mover
在畫面上移動。程式如下:
Example 1.7: Motion 101 (Velocity)
class Mover:
def __init__(self):
# 取得顯示畫面的大小
self.screen = pygame.display.get_surface()
self.width, self.height = self.screen.get_size()
# 設定物件的起始位置
x = random.uniform(0, self.width)
y = random.uniform(0, self.height)
self.position = pygame.Vector2(x, y)
# 設定物件的初始速度
vx = random.uniform(-2, 2)
vy = random.uniform(-2, 2)
self.velocity = pygame.Vector2(vx, vy)
def update(self):
self.position += self.velocity
def show(self):
pygame.draw.circle(self.screen, (0, 0, 0), self.position, 24)
def check_edges(self):
if self.position.x > self.width:
self.position.x = 0
elif self.position.x < 0:
self.position.x = self.width
if self.position.y > self.height:
self.position.y = 0
elif self.position.y < 0:
self.position.y = self.height
# python version 3.10.9
import random
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Example 1.7: Motion 101 (Velocity)")
WHITE = (255, 255, 255)
screen_size = (640, 360)
screen = pygame.display.set_mode(screen_size)
FPS = 60
frame_rate = pygame.time.Clock()
mover = Mover()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
mover.update()
mover.check_edges()
mover.show()
pygame.display.update()
frame_rate.tick(FPS)
Motion 101這個例子其實挺單調的,因為那顆球永遠都是氣定神閒地以相同的速度移動著,既不會加速,也不會減速。想要讓球的移動方式更多變、更真實一些,就必須引入加速度(acceleration)這個大家耳熟能詳的物理量。