The Nature of Code閱讀心得與Python實作:4.2 A Single Particle

閱讀時間約 1 分鐘

在開始真正處理粒子系統之前,得先寫個用來描述單一粒子的類別。這個類別,就把它叫做Particle

對我們而言,一個粒子就是一個在畫面上移動的獨立物體,它有位置、速度、加速度。所以,在Particle這個類別中,應該要有用來紀錄粒子的位置、速度、加速度等資訊的變數,以及更新這些資訊的方法。當然啦,把粒子顯示到畫面上的方法,也是一定要有的。這些林林總總的變數、方法,在先前第二章所建立的Mover類別中都有;既然如此,那就把Mover類別當成建造Particle類別的模板,稍微改一改,並加入需要的新功能就可以了。

有些粒子系統會利用發射器(emitter)來產生粒子,並藉以設定粒子的初始狀態,讓粒子系統可以呈現出不同的模擬效果。這也是這章在設計粒子系統時,所採用的方式。

粒子系統的粒子有個不同於mover的特點,那就是粒子會出生和死亡:當粒子從發射器生出來之後,最終會因為某個事件而消失不見。這裡的「某個事件」,可以是碰到其他物體、跑出畫面,或者是已經存在夠長的時間等。為了簡化起見,在我們的Particle類別中,會先只根據粒子存在的時間長短,來決定粒子是否消失。

粒子存在時間的長短,其實就是粒子的壽命。要讓由Particle類別所製造出來的粒子有壽命,最直接的做法就是利用計數器,當計數器的數值低於某個門檻值時,就代表那個粒子該消失了。要達到這個目的,可以設定一個變數並給個初始值,然後當更新粒子的狀態時,也同時更新變數內的數值。例如,設定

lifespan = 255

當更新粒子的狀態時,執行

lifespan -= 2

就這樣不斷地倒數計時下去,直到lifespan低於門檻值時,就可以判定粒子的壽命到了,該消失了。

綜合上面的討論,Particle類別可以設計成這樣:

class Particle:
def __init__(self, x, y, mass):
# 取得顯示畫面
self.screen = pygame.display.get_surface()

# 讓傳遞進來的數值來決定particle的質量
self.mass = mass

# particle的質量越大,尺寸就會越大
self.size = 16*self.mass
self.radius = self.size/2

# particle的壽命
self.lifespan = 255

# 讓傳遞進來的數值來決定particle的起始位置
self.position = pygame.Vector2(x, y)

# 讓particle有隨機的初始速度
self.velocity = pygame.Vector2(random.uniform(-1, 1), random.uniform(-2, 0))

# particle的初始加速度
self.acceleration = pygame.Vector2(0, 0)

# 設定particle所在surface的格式為per-pixel alpha
self.surface = pygame.Surface((self.size, self.size), pygame.SRCALPHA)

def apply_force(self, force):
self.acceleration += force/self.mass

def update(self):
self.velocity += self.acceleration
self.position += self.velocity
self.acceleration *= 0

self.lifespan -= 2

def show(self):
# 使用具透明度的白色把particle所在的surface清空
self.surface.fill((255, 255, 255, 0))

# 畫出具有透明度的particle,並根據particle剩下的壽命長短來決定透明度
color = (0, 0, 0, self.lifespan)
center = (self.radius, self.radius)
pygame.draw.circle(self.surface, color, center, self.radius)

# 把particle所在的surface貼到最後要顯示的畫面上
self.screen.blit(self.surface, self.position-center)

def is_dead(self):
return self.lifespan < 0

在這裡,我們用lifespan裡頭的數值來決定粒子的透明度,數值越小越透明;這樣隨著時間過去,粒子就會逐漸淡出畫面,最後完全看不見,就像是消失了一樣。

新加入的is_dead()方法,就是用來判斷粒子是不是壽命已到,該消失了。

下面這個例子,會在畫面上產生一個受重力作用而往下掉的粒子。隨著時間過去,粒子的顏色會越來越淡,最後消失不見。當粒子消失之後,會再產生一個新的粒子,繼續重複這個過程。

Example 4.1: A Single Particle

# python version 3.10.9
import random
import sys

import pygame # version 2.3.0


pygame.init()

pygame.display.set_caption("Example 4.1: A Single Particle")

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.1)

particle = Particle(320, 50, 1)

while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()

screen.fill(WHITE)

particle.apply_force(gravity)

particle.update()
if particle.is_dead():
# 原來的粒子壽終正寢之後,生個新的粒子出來取代它
particle = Particle(320, 50, 1)
else:
particle.show()

pygame.display.update()
frame_rate.tick(FPS)

Exercise 4.1

def run(self, force):
self.apply_force(force)
self.update()
self.show()

主程式部分,把

particle.apply_force(gravity)
particle.update()
particle.show()

改成

particle.run(gravity)

這樣子寫,除了主程式可以比較簡短之外,看不出來有什麼其他的好處;嚴格來說,這樣寫反而會帶來一些問題。其中一個不那麼嚴重的問題是,因為把三個方法綁在一起,當需要處理更多樣化的作用力時,會因為比較沒有彈性而綁手綁腳的。

除了比較沒彈性的問題之外,上述寫法另一個比較嚴重的問題,是可能會把已經壽終正寢應該移除的粒子,給顯示在畫面上。怎麼說呢?依照原書在的做法,當更新粒子狀態之後,緊接著就會把粒子顯示在畫面上,然後再移除壽命已到的粒子。這也就是說,即便粒子在更新狀態之後的lifespan值已經低於設定好的門檻值,它還是會被顯示在畫面上,然後才被移除;這樣子的處理順序,顯然是有問題的。

既然原書的寫法有上述的問題,所以在寫Example 4.1時,就採用不同的寫法來加以避免。什麼樣的寫法呢?其實也就是把處理的順序稍微調整一下而已:在更新粒子狀態之後,就把壽命已到的粒子移除,然後再把還活著的粒子顯示在畫面上。依照這樣子的順序來處理,就不會把壽命已到的粒子顯示出來了。

Exercise 4.2

class Particle:
def __init__(self, x, y, mass):
# 取得顯示畫面
self.screen = pygame.display.get_surface()

# 讓傳遞進來的數值來決定particle的質量
self.mass = mass

# particle的質量越大,尺寸就會越大
self.size = 16*self.mass
self.radius = self.size/2

# particle的壽命
self.lifespan = 255

# 讓傳遞進來的數值來決定particle的起始位置
self.position = pygame.Vector2(x, y)

# 讓particle有隨機的初始速度
self.velocity = pygame.Vector2(random.uniform(-1, 1), random.uniform(-2, 0))

# particle的初始加速度
self.acceleration = pygame.Vector2(0, 0)

# particle的角速度,單位是「度」。向右飛時順時針旋轉;向左飛時逆時針旋轉
self.angular_velocity = -10*self.velocity.x

# particle的初始角度,單位是「度」
self.angle_deg = 0

# 設定particle所在surface的格式為per-pixel alpha
self.surface = pygame.Surface((self.size, self.size), pygame.SRCALPHA)

def apply_force(self, force):
self.acceleration += force/self.mass

def update(self):
self.velocity += self.acceleration
self.position += self.velocity
self.acceleration *= 0

self.lifespan -= 2

self.angle_deg += self.angular_velocity

def show(self):
# 使用具透明度的白色把particle所在的surface清空
self.surface.fill((255, 255, 255, 0))

# 畫出具有透明度的particle,並根據particle剩下的壽命長短來決定透明度
rect = pygame.Rect(0, 0, self.size, self.size)
color = (0, 0, 0, self.lifespan)
pygame.draw.rect(self.surface, color, rect)

# 旋轉角度的單位需由弳度轉換為度
rotated_surface = pygame.transform.rotate(self.surface, self.angle_deg)
rect_new = rotated_surface.get_rect(center=self.position)

# 把particle所在的surface貼到最後要顯示的畫面上
self.screen.blit(rotated_surface, rect_new)

def is_dead(self):
return self.lifespan < 0


# python version 3.10.9
import random
import sys

import pygame # version 2.3.0


pygame.init()

pygame.display.set_caption("Exercise 4.2")

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)

particle = Particle(320, 50, 1)

while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()

screen.fill(WHITE)

particle.apply_force(gravity)

particle.update()
if particle.is_dead():
# 原來的粒子壽終正寢之後,生個新的粒子出來取代它
particle = Particle(320, 50, 1)
else:
particle.show()

pygame.display.update()
frame_rate.tick(FPS)


15會員
119內容數
寫點東西自娛娛人
留言0
查看全部
發表第一個留言支持創作者!
ysf的沙龍 的其他內容
之所以要研究粒子系統,除了可以用來模擬許多自然界中的現象之外,另一個更重要的原因是:在我們的模擬世界中,會有許多物體存在,而這些物體可能會形成一群一群的群體
粒子系統(particle system)指的是,由許多微小粒子組成,呈現出模糊外觀的物體。這一章的重點會放在探討利用物件導向技術實作粒子系統時,該採用什麼樣的程式架構、描述個別粒子和整個系統的資料該如何管理等方面。
這一節要模擬的是擺(pendulum)這個裝置中,構造最簡單、具有理想化性質的單擺(simple pendulum)。
我們曾經利用sin函數來模擬彈簧吊錘(bob)的運動,雖然這樣子的做法程式很容易寫,但是卻沒辦法模擬彈簧吊錘受到如風力、重力等環境中其他作用力的影響下,在空間中的運動狀況。要克服這樣子的問題,就不能再倚靠sin函數,而必須改用能夠用來計算彈簧彈力的虎克定律(Hooke's law)。
在x軸上依序取一些點,然後把這些點以及其對應的sin函數的值所構成的二維座標點畫出來時,就可以看到由這個sin函數所產生的像波一樣的圖案,也就是波型(wave pattern)。不同樣式的波型,可以用來設計生物的軀幹或肢體,也可以用來模擬像水這類柔軟的表面。
藉由設定振幅、頻率、週期等性質,我們可以模擬出真實世界中的振盪現象。其實,用稍微簡單一點的方式來處理,依舊可以得到相同的效果。
之所以要研究粒子系統,除了可以用來模擬許多自然界中的現象之外,另一個更重要的原因是:在我們的模擬世界中,會有許多物體存在,而這些物體可能會形成一群一群的群體
粒子系統(particle system)指的是,由許多微小粒子組成,呈現出模糊外觀的物體。這一章的重點會放在探討利用物件導向技術實作粒子系統時,該採用什麼樣的程式架構、描述個別粒子和整個系統的資料該如何管理等方面。
這一節要模擬的是擺(pendulum)這個裝置中,構造最簡單、具有理想化性質的單擺(simple pendulum)。
我們曾經利用sin函數來模擬彈簧吊錘(bob)的運動,雖然這樣子的做法程式很容易寫,但是卻沒辦法模擬彈簧吊錘受到如風力、重力等環境中其他作用力的影響下,在空間中的運動狀況。要克服這樣子的問題,就不能再倚靠sin函數,而必須改用能夠用來計算彈簧彈力的虎克定律(Hooke's law)。
在x軸上依序取一些點,然後把這些點以及其對應的sin函數的值所構成的二維座標點畫出來時,就可以看到由這個sin函數所產生的像波一樣的圖案,也就是波型(wave pattern)。不同樣式的波型,可以用來設計生物的軀幹或肢體,也可以用來模擬像水這類柔軟的表面。
藉由設定振幅、頻率、週期等性質,我們可以模擬出真實世界中的振盪現象。其實,用稍微簡單一點的方式來處理,依舊可以得到相同的效果。
你可能也想看
Google News 追蹤
Thumbnail
本專欄將提供給您最新的市場資訊、產業研究、交易心法、精選公司介紹,以上內容並非個股分析,還請各位依據自身狀況作出交易決策。歡迎訂閱支持我,獲得相關內容,也祝您的投資之路順遂! 每年 $990 訂閱方案👉 https://reurl.cc/VNYVxZ 每月 $99 訂閱方案👉https://re
到目前為止,我們所模擬的萬有引力,是一個物體吸引另一個物體,或者是一個物體吸引多個物體。然而,在真實世界中,每個物體都會互相吸引,所以在這一節中,就來把模擬的情境,擴展成多個物體互相吸引。
Thumbnail
模擬世界是我們寫程式造出來的,我們就是模擬世界的主宰,所以各種作用力要長什麼樣子、要怎麼個作用法,都由我們決定。不過,如果希望這些作用力看起來像真實世界的作用力一樣,那在寫程式的時候,套用這些作用力在真實世界中的物理公式,會是比較省時省力的做法。
到目前為止,為了簡化問題,我們都假設物體的質量是1。接下來,我們將移除這個假設,然後將完全符合牛頓第二運動定律的apply_force()方法,整合到Mover這個類別中。
這一節談的是牛頓的三大運動定律,以及力對於物體運動狀態的影響。
介紹以物件導向的方式,以向量來實作物體運動的模擬程式。
介紹如何在模擬物體運動時,引入加速度這個物理量。
Thumbnail
這一節談的是向量的定義,以及如何運用向量來建立模擬物體運動時,關於位置和速度間的關係式。
這一章介紹向量(vector)這個在物理、工程等領域非常重要的數學工具,以及如何用它來模擬一些物理現象。
Thumbnail
本專欄將提供給您最新的市場資訊、產業研究、交易心法、精選公司介紹,以上內容並非個股分析,還請各位依據自身狀況作出交易決策。歡迎訂閱支持我,獲得相關內容,也祝您的投資之路順遂! 每年 $990 訂閱方案👉 https://reurl.cc/VNYVxZ 每月 $99 訂閱方案👉https://re
到目前為止,我們所模擬的萬有引力,是一個物體吸引另一個物體,或者是一個物體吸引多個物體。然而,在真實世界中,每個物體都會互相吸引,所以在這一節中,就來把模擬的情境,擴展成多個物體互相吸引。
Thumbnail
模擬世界是我們寫程式造出來的,我們就是模擬世界的主宰,所以各種作用力要長什麼樣子、要怎麼個作用法,都由我們決定。不過,如果希望這些作用力看起來像真實世界的作用力一樣,那在寫程式的時候,套用這些作用力在真實世界中的物理公式,會是比較省時省力的做法。
到目前為止,為了簡化問題,我們都假設物體的質量是1。接下來,我們將移除這個假設,然後將完全符合牛頓第二運動定律的apply_force()方法,整合到Mover這個類別中。
這一節談的是牛頓的三大運動定律,以及力對於物體運動狀態的影響。
介紹以物件導向的方式,以向量來實作物體運動的模擬程式。
介紹如何在模擬物體運動時,引入加速度這個物理量。
Thumbnail
這一節談的是向量的定義,以及如何運用向量來建立模擬物體運動時,關於位置和速度間的關係式。
這一章介紹向量(vector)這個在物理、工程等領域非常重要的數學工具,以及如何用它來模擬一些物理現象。