集合許多個別的粒子可以形成一個粒子系統,那集合許多粒子系統,是不是也能形成一個系統呢?
當然可以!
既然集合許多粒子系統可以形成一個系統,那集合許多由粒子系統形成的系統,可不可以又形成一個系統呢?
當然也可以!
接下來,我們會以同樣的思路來處理含有許多粒子系統的系統。
在上一節中,我們讓許多粒子形成一個發射器系統;在這一節中,我們會來處理由許多發射器所構成的系統。
在Example 4.3中,我們處理的是單一的粒子系統:畫面上就只有一個不斷冒出粒子的發射器粒子系統。程式的寫法,是把那個系統指定給一個變數emitter
,然後不斷把新的粒子加進emitter
中,並呼叫emitter
的run()
方法:
現在,如果我們希望一開始的時候畫面上沒有任何發射器粒子系統,然後每當按下滑鼠左鍵時,就會在滑鼠游標所在的地方放置一個發射器粒子系統,那程式要怎麼寫呢?在設計Emitter
這個類別時,我們是把系統所含的粒子放在一個list
中。現在的做法也一樣,就把粒子系統放在一個叫做emitters
的list
中來處理;當按下滑鼠左鍵時,就新增一個發射器粒子系統到list
中,然後針對所有在list
中的發射器粒子系統,逐一新增粒子,並呼叫run()
方法。
Example 4.4: A System of Systems
# python version 3.10.9
import random
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Example 4.4: A System of Systems")
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)
# 一開始的時候,畫面上沒有任何粒子系統
emitters = []
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
# 當按下滑鼠左鍵時,就把粒子系統新增到list中
if pygame.mouse.get_pressed()[0]:
x, y = pygame.mouse.get_pos()
emitters.append(Emitter(x, y, 1))
# 針對所有在list中的粒子系統,逐一新增粒子,並呼叫run()方法
for emitter in emitters:
emitter.add_particle()
emitter.run()
pygame.display.update()
frame_rate.tick(FPS)
Exercise 4.5
在Emitter
類別的__init__()
加入紀錄粒子庫存數量的變數:
self.inventory = 100
修改add_particle()
def add_particle(self):
if self.inventory > 0:
self.particles.append(Particle(self.origin.x, self.origin.y, self.mass))
self.inventory -= 1
主程式如下:
# python version 3.10.9
import random
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Exercise 4.5")
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)
emitters = []
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
if pygame.mouse.get_pressed()[0]:
x, y = pygame.mouse.get_pos()
emitters.append(Emitter(x, y, 1))
for emitter in emitters:
emitter.add_particle()
emitter.run()
# 移除已經沒有粒子的粒子系統
emitters = list(filter(lambda emitter: bool(emitter.particles), emitters))
pygame.display.update()
frame_rate.tick(FPS)
Exercise 4.6
一開始的時候,讓粒子系統的粒子聚在一起,形成一個大的團塊。這時候,僅在畫面上顯示粒子,而不更新其狀態,等偵測到滑鼠點擊團塊時,才開始更新粒子的狀態。
修改Emitter
類別。
修改__init__()
方法,讓粒子系統的粒子在一開始的時候,就在畫面上結成團塊,同時加入用來紀錄粒子系統狀態的變數,以便判斷粒子系統是否正碎裂四散。
def __init__(self, x, y, mass):
self.mass = mass
self.size = 16*self.mass
# 一開始的粒子團塊
self.particles = [Particle(x+3*i, y+3*j, mass)
for i in range(10) for j in range(10)]
# 發射器位置
self.origin = pygame.Vector2(x, y)
# 粒子系統是否正碎裂四散?
self.shattering = False
修改run()
方法,加入判斷粒子系統是否正碎裂四散的功能
def run(self):
if not self.shattering:
for particle in self.particles:
particle.show()
else:
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()
新增check_click()
方法,用來偵測滑鼠是否點擊粒子團塊。當滑鼠點擊任何團塊中的粒子時,即表示滑鼠正點擊該粒子團塊。
def check_click(self, x, y):
for particle in self.particles:
d = pygame.Vector2(x, y).distance_to(particle.position)
if d <= particle.radius:
self.shattering = True
break
主程式如下:
# python version 3.10.9
import random
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Exercise 4.6")
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)
emitters = [Emitter(100*i, 130, 1) for i in range(1, 6)]
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
for emitter in emitters:
if pygame.mouse.get_pressed()[0]:
x, y = pygame.mouse.get_pos()
emitter.check_click(x, y)
emitter.run()
pygame.display.update()
frame_rate.tick(FPS)