這一節的標題是
4.7 Particle Systems with Forces
因為方格子標題字數限制,所以沒完整顯現
到目前為止,我們所設計的粒子系統,都只有受到重力的作用。那如果作用力是風力呢?又該怎麼設計?
原書在處理作用在粒子系統上的重力時,是在Particle
類別中設計一個run()
方法,並把重力的作用寫死在裡頭,像這樣:
def run(self):
gravity = pygame.Vector2(0, 0.05)
self.apply_force(gravity)
self.update()
self.show()
當粒子系統中的粒子呼叫這個方法時,就能把重力作用在粒子上。
這個寫法的主要缺點,就是太沒彈性了。如果現在作用力要改成風力,那就得去改run()
裡頭的東西。除此之外,另一個缺點是,在主程式裡頭,是看不到重力存在的。
因為覺得原書的寫法不是很好,所以改成在主程式中設定gravity
,然後在Emitter
類別的run()
方法中處理重力:
def run(self):
for particle in self.particles:
particle.apply_force(gravity)
particle.update()
# 移除壽命已到的粒子
self.particles = list(filter(lambda particle: not particle.is_dead(), self.particles))
for particle in self.particles:
particle.show()
這樣子gravity
就會以全域變數的身份出現在run()
這個方法中。這樣寫的好處是,可以清楚的在主程式中看到有個作用力存在,而且可以進行設定、調整。
雖然讓gravity
以全域變數的身份出現在Emitter
類別的run()
方法中有些好處,但是也有些缺點;最大的缺點是,在主程式中可以看到有個作用力,但卻看不出這個作用力是作用在哪裡。另外,眾所周知,使用全域變數,始終就不是個好主意,除了程式比較雜亂難以閱讀之外,在除錯時也比較難以找到bug。
既然兩種寫法都不是那麼的好,那有沒有比較好的寫法呢?
這個問題沒有標準答案,不同的情境可能會有各自比較好的寫法。下面介紹的是原書提供的寫法,可以避免前面提到的那兩種寫法的一些問題,但要注意的是,並非在所有的情境下,都一定要這樣寫。
既然粒子系統的頭頭是Emitter
這個類別,所有粒子都歸它管,那就幫它加個apply_force()
方法,用來讓作用力作用在系統內的所有粒子上:
def apply_force(self, force):
for particle in self.particles:
particle.apply_force(force)
當在主程式中要讓作用力作用在粒子系統上時,只要呼叫這個方法就可以了:
gravity = pygame.Vector2(0, 0.05)
:
:
emitter.add_particle()
emitter.apply_force(gravity)
emitter.run()
當然,這時候run()
方法裡頭對粒子施加重力的部分,也就是
particle.apply_force(gravity)
就必須拿掉。
把Emitter
類別處理作用力的部分改成這樣寫,不僅主程式清楚明瞭,而且寫起來彈性也比較大,下面這個例子就是採用這樣子的寫法。
Example 4.6: A Particle System with Force
因為Particle
類別並沒有任何更動,所以就只列出Emitter
類別和主程式。
class Emitter:
def __init__(self, x, y, mass):
self.mass = mass
self.size = 16*self.mass
self.particles = []
# 發射器位置
self.origin = pygame.Vector2(x, y)
def add_particle(self):
self.particles.append(Particle(self.origin.x, self.origin.y, self.mass))
def apply_force(self, force):
for particle in self.particles:
particle.apply_force(force)
def run(self):
for particle in self.particles:
particle.apply_force(gravity)
particle.update()
# 移除壽命已到的粒子
self.particles = list(filter(lambda particle: not particle.is_dead(), self.particles))
for particle in self.particles:
particle.show()
# python version 3.10.9
import random
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Example 4.6: A Particle System with Forces")
WHITE = (255, 255, 255)
screen_size = 640, 360
screen = pygame.display.set_mode(screen_size)
FPS = 60
frame_rate = pygame.time.Clock()
gravity = pygame.Vector2(0, 0.05)
emitter = Emitter(320, 50, 1)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
emitter.add_particle()
emitter.apply_force(gravity)
emitter.run()
pygame.display.update()
frame_rate.tick(FPS)