更新於 2024/10/18閱讀時間約 8 分鐘

The Nature of Code閱讀心得與Python實作:4.7 Particle Systems with...

這一節的標題是
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)


分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.