2024年2月29日 星期四

2.2 Force Accumulation

這一節要來看看,有許多個力同時作用時,該怎麼處理。

如果想要讓wind和gravity這兩個力同時作用在mover上,程式可以這樣寫

mover.apply_force(wind+gravity)

這樣子的寫法,其實就是先算出合力,然後讓合力作用在mover上。

另一種處理合力的方法,是將apply_force()改寫為

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

這樣當每次呼叫apply_force()時,新的作用力就會累加進去,而不是取代掉前面的作用力。所以,上一節程式的寫法,就可以達到讓wind和gravity同時作用在mover上的目的。後續的內容中,我們都會採用這種處理方式。

處理完合力的問題之後,還有個地方必須要處理,否則mover將會感受到永不停止的作用力。

假設當我們按下滑鼠左鍵時,會有一陣風吹向mover,而當放開滑鼠左鍵時,風就停止。寫成程式就是

if pygame.mouse.get_pressed()[0]:
    mover.apply_force(wind)

放開滑鼠左鍵後,因為已經沒有風了,所以沒有力量作用在mover上,它的加速度會是0。這時候,根據牛頓第一運動定律,mover應該要維持等速運動。但是,當我們呼叫update()來計算mover的速度和位置時,因為update()的寫法是

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

這時候acceleration的值,還是維持著呼叫apply_force()時所得到的值,即便來自wind的作用力早就不存在了。這顯然不是我們所要的結果,我們要的是當合力為0時,acceleration的值是0。那程式該怎麼寫呢?其實挺簡單的,在update()的最後,讓acceleration的值歸零就可以了:

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

這樣子,對於acceleration來說,每一幀畫面都是新的開始,不會留有從上一幀畫面而來的記憶。在現在這一幀畫面中有多大的作用力,mover的加速度就會有多大的值,完全符合牛頓的運動定律。

到目前為止,在利用牛頓運動定律來模擬物體的運動時,有兩個很重要,會影響模擬精準度的因素,我們尚未觸及:物體的質量與時間步長(time step)。物體的質量這個因素,會在下一節討論,現在先來瞭解一下什麼是時間步長。

時間步長就是模擬時的更新速率。時間步長一般記做dt,指的是「delta time」、「時間差」的意思,它的大小會影響模擬的準確度,而這也是為什麼許多物理引擎會提供可以調整時間步長大小功能的原因。

一直到第六章介紹物理引擎之前,我們都會假設,主程式中跑完一次while迴圈,就相當於一個時間步長。這個假設可能不是最準確的,但卻可以讓我們能夠專注於瞭解,在模擬時所用到的主要原理。

Exercise 2.1

import noise
import pygame
import random
import sys

class Balloon:
    def __init__(self):
        self.screen = pygame.display.get_surface()
        self.width, self.height = self.screen.get_size()

        self.size = 16

        self.position = pygame.Vector2(self.width//2, self.height-self.size)
        self.velocity = pygame.Vector2(0, 0)
        self.acceleration = pygame.Vector2(0, 0)

    def apply_force(self, force):
        self.acceleration += force
        
    def update(self):
        self.velocity += self.acceleration
        self.position += self.velocity
        self.acceleration *= 0
        
    def show(self):
        pygame.draw.circle(self.screen, (200, 0, 0), self.position, self.size)
     
    def check_edges(self):
        restitution = 0.5  # 非彈性碰撞恢復係數
        condition1 = (self.position.x+self.size > self.width) and (self.velocity.x > 0)
        condition2 = (self.position.x-self.size < 0) and (self.velocity.x < 0)
        if condition1 or condition2:
            self.velocity.x = -restitution*self.velocity.x
            self.acceleration.x = -restitution*self.acceleration.x

        condition1 = (self.position.y+self.size > self.height) and (self.velocity.y > 0)
        condition2 = (self.position.y-self.size < 0) and (self.velocity.y < 0)        
        if condition1 or condition2:
            self.velocity.y = -restitution*self.velocity.y
            self.acceleration.y = -restitution*self.acceleration.y


pygame.init()

pygame.display.set_caption("Exercise 2.1")

FPS = 60
WHITE = (255, 255, 255)

screen_size = WIDTH, HEIGHT = 640, 360
screen = pygame.display.set_mode(screen_size)

frame_rate = pygame.time.Clock()

balloon = Balloon()
buoyancy = pygame.Vector2(0, -0.01)
wind_intensity = 0.03
xoff, yoff = 0, 10000
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    
    screen.fill(WHITE)    

    xoff, yoff = xoff+0.01, yoff+0.01
    wind = wind_intensity*pygame.Vector2(noise.pnoise1(xoff), noise.pnoise1(yoff))

    balloon.apply_force(buoyancy)
    balloon.apply_force(wind)
    balloon.update()
    balloon.check_edges()
    balloon.show()
        
    pygame.display.update()
    frame_rate.tick(FPS) 

沒有留言:

張貼留言

4.9 Image Textures and Additive Blending

粒子系統可以用來製作視覺特效(visual effect, VFX),而怎麼呈現粒子的外觀,以及粒子具有怎樣的紋理(texture),都會影響特效所展現出來的效果。例如在下圖中,可以很清楚地看到,使用兩種不同的粒子紋理所呈現出來的特效效果,就有很大的不同。 要賦予粒子不同的紋理...