The Nature of Code閱讀心得與Python實作:4.4 A Particle Emitter

更新 發佈閱讀 20 分鐘

到目前為止,我們已經建立了描述單一粒子的Particle類別,並且能夠利用list這個資料結構,來處理由Particle類別產生的大量粒子物件。在這一節,我們要把這一些整合起來,建立一個Emitter類別。有了這個類別,就可以讓主程式寫起來長成這個樣子:

emitter = Emitter()

while True:
:
:
emitter.run()

除此之外,也可以讓我們能夠比較輕鬆地一次處理多個由許多粒子構成的系統。

這個Emitter類別,就是在4.1節提到的ParticleSystem類別,但加入了一個新功能,用來設定產生粒子的位置。所以,這個Emitter類別,其實也就是4.2節提到的粒子的發射器。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 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()

要注意的是,在run()方法裡頭,作用在粒子上的重力gravity,是個全域變數;這樣子的寫法顯然不怎麼好。不過,在目前的架構下,好像也只能這樣寫了。

下面這個例子,就是利用Emitter類別,在畫面的指定位置,不斷地噴發出粒子。

Example 4.3: A Single Particle Emitter

# python version 3.10.9
import random
import sys

import pygame # version 2.3.0


pygame.init()

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

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.run()

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

Exercise 4.3

從滑鼠游標發射粒子。

# python version 3.10.9
import random
import sys

import pygame # version 2.3.0


pygame.init()

pygame.display.set_caption("Exercise 4.3")

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)

x, y = pygame.mouse.get_pos()
emitter = Emitter(x, y, 1)

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

screen.fill(WHITE)

x, y = pygame.mouse.get_pos()
emitter.origin = pygame.Vector2(x, y)

emitter.add_particle()
emitter.run()

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

Exercise 4.4

raw-image

修改Spaceship類別。完整的Spaceship類別程式碼以及主程式如下:

class Spaceship:
def __init__(self, mass=1, heading=90):
# 取得顯示畫面及其大小
self.screen = pygame.display.get_surface()
self.width, self.height = self.screen.get_size()

self.mass = mass

# 航向,單位是「度」
self.heading = heading # in degree

# 起始位置在畫面中央
x, y = self.width//2, self.height//2
self.position = pygame.Vector2(x, y)

# 最高速率
self.top_speed = 5

# 起始速度、加速度
self.velocity = pygame.Vector2(0, 0)
self.acceleration = pygame.Vector2(0, 0)

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

# 在surface上畫出spaceship
body = [(35, 15), (5, 0), (5, 30)]
pygame.draw.polygon(self.surface, (0, 0, 0), body)

nozzle1 = pygame.Rect(0, 5, 5, 5)
pygame.draw.rect(self.surface, (0, 0, 0), nozzle1)

nozzle2 = pygame.Rect(0, 20, 5, 5)
pygame.draw.rect(self.surface, (0, 0, 0), nozzle2)

# 噴嘴發射器位置
nozzle1_location = pygame.Vector2(0, 5+5/2)
nozzle2_location = pygame.Vector2(0, 20+5/2)

# 建造發射器
emitter1 = Emitter(nozzle1_location.x, nozzle1_location.y, 0.3)
emitter2 = Emitter(nozzle2_location.x, nozzle2_location.y, 0.3)
self.emitters = [emitter1, emitter2]

# 中心點至噴嘴發射器向量,用於計算在不同航向時,發射器在顯示畫面上之位置
ship_center = pygame.Vector2(35/2, 30/2)
nozzle1_vec = nozzle1_location - ship_center
nozzle2_vec = nozzle2_location - ship_center
self.nozzle_vecs = [nozzle1_vec, nozzle2_vec]

self.thrusting = False

def set_heading(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.heading += 5
elif keys[pygame.K_RIGHT]:
self.heading -= 5

def generate_thrust(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_z]:
self.thrusting = True
thrust = pygame.Vector2.from_polar((1, self.heading))
thrust.scale_to_length(0.1)
thrust.y = -thrust.y
else:
self.thrusting = False
# 沒有按下z鍵,減速到停下為止
if spaceship.velocity.length() > 1e-5:
thrust = -spaceship.velocity
thrust.scale_to_length(0.05)
else:
thrust = pygame.Vector2(0, 0)

return thrust

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

def update(self):
self.velocity += self.acceleration
if self.velocity.length() < 1e-5:
# 速度很小時,直接判定為靜止狀態
self.velocity *= 0
else:
# 限速
if self.velocity.length() > self.top_speed:
self.velocity.scale_to_length(self.top_speed)

self.position += self.velocity

self.acceleration *= 0

def show(self):
rotated_surface = pygame.transform.rotate(self.surface, self.heading)
rect_new = rotated_surface.get_rect(center=self.position)

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

if self.thrusting:
emitter_direction = pygame.Vector2.from_polar((1, self.heading+180))
emitter_direction.y = -emitter_direction.y

# 計算發射器在顯示畫面上之位置,並裝填及設定粒子之速度、壽命
for i in range(2):
self.emitters[i].origin = self.position + self.nozzle_vecs[i].rotate(-self.heading)
self.emitters[i].add_particle()
self.emitters[i].particles[-1].velocity = 0.5*emitter_direction
self.emitters[i].particles[-1].lifespan = 100

for emitter in self.emitters:
emitter.run()

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("Exercise 4.4")

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)

spaceship = Spaceship()

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

screen.fill(WHITE)

spaceship.set_heading()

thrust = spaceship.generate_thrust()
spaceship.apply_force(thrust)

spaceship.update()
spaceship.check_edges()
spaceship.show()

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


留言
avatar-img
留言分享你的想法!
avatar-img
ysf的沙龍
19會員
157內容數
寫點東西自娛娛人
ysf的沙龍的其他內容
2025/04/04
基礎CA的規則集總共有256個,經由這些規則集所生成的圖案,大部分看起來都平平無奇;不過,還是有些圖案真的是會讓人驚嘆不已,因為實在是跟自然界中可以看到的圖案樣式很相像。根據這些圖案的樣式和特性,Wolfram把它們分成四大類,接下來就來看看,這四大類的圖案各有怎樣的樣式和特性。
Thumbnail
2025/04/04
基礎CA的規則集總共有256個,經由這些規則集所生成的圖案,大部分看起來都平平無奇;不過,還是有些圖案真的是會讓人驚嘆不已,因為實在是跟自然界中可以看到的圖案樣式很相像。根據這些圖案的樣式和特性,Wolfram把它們分成四大類,接下來就來看看,這四大類的圖案各有怎樣的樣式和特性。
Thumbnail
2025/03/31
這節介紹Wolfram的基礎CA (elementary CA)設計方式,以及其迭代過程所構成的圖案。
Thumbnail
2025/03/31
這節介紹Wolfram的基礎CA (elementary CA)設計方式,以及其迭代過程所構成的圖案。
Thumbnail
2025/03/21
細胞自動機的英文是cellular automaton,複數型態為cellular automata,簡稱CA,是一種由細胞物件所組成的系統模型。
Thumbnail
2025/03/21
細胞自動機的英文是cellular automaton,複數型態為cellular automata,簡稱CA,是一種由細胞物件所組成的系統模型。
Thumbnail
看更多
你可能也想看
Thumbnail
還在煩惱平凡日常該如何增添一點小驚喜嗎?全家便利商店這次聯手超萌的馬來貘,推出黑白配色的馬來貘雪糕,不僅外觀吸睛,層次豐富的雙層口味更是讓人一口接一口!本文將帶你探索馬來貘雪糕的多種創意吃法,從簡單的豆漿燕麥碗、藍莓果昔,到大人系的奇亞籽布丁下午茶,讓可愛的馬來貘陪你度過每一餐,增添生活中的小確幸!
Thumbnail
還在煩惱平凡日常該如何增添一點小驚喜嗎?全家便利商店這次聯手超萌的馬來貘,推出黑白配色的馬來貘雪糕,不僅外觀吸睛,層次豐富的雙層口味更是讓人一口接一口!本文將帶你探索馬來貘雪糕的多種創意吃法,從簡單的豆漿燕麥碗、藍莓果昔,到大人系的奇亞籽布丁下午茶,讓可愛的馬來貘陪你度過每一餐,增添生活中的小確幸!
Thumbnail
本文介紹了Python中的物件導向程式設計的重要概念,包括類別、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射。每個概念都有對應的程式碼範例來說明其用法和功能。這些概念對於理解和使用Python進行物件導向程式設計至關重要。
Thumbnail
本文介紹了Python中的物件導向程式設計的重要概念,包括類別、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射。每個概念都有對應的程式碼範例來說明其用法和功能。這些概念對於理解和使用Python進行物件導向程式設計至關重要。
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
當你需要在 Python 中執行多個任務,但又不希望它們相互阻塞時,可以使用 threading 模組。 threading 模組允許你在單個程序中創建多個執行緒,這些執行緒可以同時運行,從而實現並行執行多個任務的效果。
Thumbnail
當你需要在 Python 中執行多個任務,但又不希望它們相互阻塞時,可以使用 threading 模組。 threading 模組允許你在單個程序中創建多個執行緒,這些執行緒可以同時運行,從而實現並行執行多個任務的效果。
Thumbnail
本文讓我們來淺談一下類別是什麼? 若想看詳細一點的python官方教學可點此連結 Python 的類別(Class)是一種面向物件導向程式設計的概念,讓你能夠創建具有屬性和方法的物件。類別是對現實世界中事物的抽象,它包含數據和操作這些數據的方法。它非常的抽象,想像一個類別就像是一個蛋糕模具,
Thumbnail
本文讓我們來淺談一下類別是什麼? 若想看詳細一點的python官方教學可點此連結 Python 的類別(Class)是一種面向物件導向程式設計的概念,讓你能夠創建具有屬性和方法的物件。類別是對現實世界中事物的抽象,它包含數據和操作這些數據的方法。它非常的抽象,想像一個類別就像是一個蛋糕模具,
Thumbnail
關於多執行緒/多行程的使用方式 在Python 3.2版本之後加入了「concurrent.futures」啟動平行任務, 它可以更好的讓我們管理多執行緒/多行程的應用場景,讓我們在面對這種併發問題時可以不必害怕, 用一個非常簡單的方式就能夠處裡, 底下我們將為您展示一段程式碼: imp
Thumbnail
關於多執行緒/多行程的使用方式 在Python 3.2版本之後加入了「concurrent.futures」啟動平行任務, 它可以更好的讓我們管理多執行緒/多行程的應用場景,讓我們在面對這種併發問題時可以不必害怕, 用一個非常簡單的方式就能夠處裡, 底下我們將為您展示一段程式碼: imp
Thumbnail
寫程式不僅只是能動, 更要能夠看得懂, 如果我們的程式碼可以更貼近人類能懂的語言時, 後續的維護肯定會大幅度的減少成本, 想想我們回頭看看三個月前的程式碼是什麼感受吧…😫😫😫, 為了避免這樣的窘境, 我們還真的應該好好的為我們的程式碼負責, 除了「【🔒 Python 先修班】培養良好的Cod
Thumbnail
寫程式不僅只是能動, 更要能夠看得懂, 如果我們的程式碼可以更貼近人類能懂的語言時, 後續的維護肯定會大幅度的減少成本, 想想我們回頭看看三個月前的程式碼是什麼感受吧…😫😫😫, 為了避免這樣的窘境, 我們還真的應該好好的為我們的程式碼負責, 除了「【🔒 Python 先修班】培養良好的Cod
Thumbnail
撰寫Python的朋友都知道multithread/multiprocess能為我們帶來效能的改進,減少硬體資源的閒置,但在撰寫的過程中常常會發現到我們所設計的工作池模式會需要將「待辦清單」的工作項目當成參數傳遞進去執行, 除了「待辦清單」之外, 其餘的參數基本上都是固定的, 基於這樣的需求之下
Thumbnail
撰寫Python的朋友都知道multithread/multiprocess能為我們帶來效能的改進,減少硬體資源的閒置,但在撰寫的過程中常常會發現到我們所設計的工作池模式會需要將「待辦清單」的工作項目當成參數傳遞進去執行, 除了「待辦清單」之外, 其餘的參數基本上都是固定的, 基於這樣的需求之下
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News