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的沙龍
15會員
143內容數
寫點東西自娛娛人
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
介紹朋友新開的蝦皮選物店『10樓2選物店』,並分享方格子與蝦皮合作的分潤計畫,註冊流程簡單,0成本、無綁約,推薦給想增加收入的讀者。
Thumbnail
介紹朋友新開的蝦皮選物店『10樓2選物店』,並分享方格子與蝦皮合作的分潤計畫,註冊流程簡單,0成本、無綁約,推薦給想增加收入的讀者。
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能為我們帶來效能的改進,減少硬體資源的閒置,但在撰寫的過程中常常會發現到我們所設計的工作池模式會需要將「待辦清單」的工作項目當成參數傳遞進去執行, 除了「待辦清單」之外, 其餘的參數基本上都是固定的, 基於這樣的需求之下
Thumbnail
在這一課中,我們會介紹Python的一些進階特性,包括裝飾器、生成器和上下文管理器。 裝飾器 (Decorators) 裝飾器是一種可以修改其他函數的功能的函數。它們可以幫助你使代碼更簡潔,更Pythonic。
Thumbnail
在這一課中,我們會介紹Python的一些進階特性,包括裝飾器、生成器和上下文管理器。 裝飾器 (Decorators) 裝飾器是一種可以修改其他函數的功能的函數。它們可以幫助你使代碼更簡潔,更Pythonic。
Thumbnail
在第十二課中,我們將學習 Python 中的模塊和包的概念。 模塊和包是 Python 中組織程式碼的主要方式,它們讓你能夠以邏輯和易於管理的方式組織你的程式碼。
Thumbnail
在第十二課中,我們將學習 Python 中的模塊和包的概念。 模塊和包是 Python 中組織程式碼的主要方式,它們讓你能夠以邏輯和易於管理的方式組織你的程式碼。
Thumbnail
一個真實世界中的支持“讓孩童以更有趣的方式學習電腦”例子,只不過這篇演講,則是駭客等級程式專家們的 Bytes code 教學。不過這篇演講也說明了學習程式甚至參加程式會議,一樣可以樂趣無窮。
Thumbnail
一個真實世界中的支持“讓孩童以更有趣的方式學習電腦”例子,只不過這篇演講,則是駭客等級程式專家們的 Bytes code 教學。不過這篇演講也說明了學習程式甚至參加程式會議,一樣可以樂趣無窮。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News