這一節的標題是
3.7 Oscillation with Angular Velocity
因為方格子標題字數限制,所以沒完整顯現
在上一節,藉由設定振幅、頻率、週期等性質,我們可以模擬出真實世界中的振盪現象。在這一節,我們會用稍微簡單一點的方式來處理,但依舊可以得到相同的效果。
在上一節,我們在處理振盪時,使用的式子是
x = amplitude*math.sin(2*math.pi*frame_count/period)
現在把它改寫成
x = amplitude*math.sin(某個慢慢增加的數值)
也就是說,我們不再乖乖的去算sin裡頭的那一串東西,而是直接把某個數字當成那一串東西算出來的結果。畢竟現在我們要的是最後的結果,而不是多大的週期和幀數可以得到這樣的結果。
結合第二節角速度的觀念,就可以用比較簡單的方式,來改寫上一節的例子。假設
angle = 0
angular_velocity = 0.05
把那個處理振盪的式子改寫成
angle += angular_velocity
x = amplitude*math.sin(angle)
這裡的angle
,就是那個慢慢增加的數值,而這也意味著,其中隱含了下面的關係式:
angle = 2*math.pi*frame_count/period
因為在每一幀畫面中,angle
的值都會加上angular_velocity
的值,所以更進一步拆解後可以發現
angular_velocity = 2*math.pi/period
移項之後得到
period = 2*math.pi/angular_velocity
這也就是說,採用這樣子的處理方式時,雖然程式中並沒有看到週期這個變數,但這並不表示週期這個變數已經被捨棄掉了,它只是在角速度的掩護下隱藏起來而已。
改寫後的程式如下:
Example 3.6: Simple Harmonic Motion II
# python version 3.10.9
import math
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Example 3.6: Simple Harmonic Motion II")
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
WIDTH, HEIGHT = screen_size = (640, 360)
screen = pygame.display.set_mode(screen_size)
screen_center = (WIDTH//2, HEIGHT//2)
FPS = 60
frame_rate = pygame.time.Clock()
amplitude = 100
angle = 0
angular_velocity = 0.05
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
# 球的位置
x = amplitude*math.sin(angle)
position = (x+WIDTH//2, HEIGHT//2)
pygame.draw.circle(screen, BLACK, position, 20)
pygame.draw.line(screen, BLACK, position, screen_center, 3)
angle += angular_velocity
pygame.display.update()
frame_rate.tick(FPS)
接下來,把這個例子再進一步推廣,並造出名為Oscillator
的類別,然後讓振盪同時沿著x軸和y軸進行。要達到這樣子的目的,會需要分別設定x軸和y軸上的振幅、角速度,還有那個慢慢增加的數值;這些用向量來處理,會方便很多。
Example 3.7: Oscillator Objects
class Oscillator:
def __init__(self):
# 取得顯示畫面的大小
self.screen = pygame.display.get_surface()
self.width, self.height = self.screen.get_size()
# 樞軸,拴住oscillator的地方
self.pivot = pygame.Vector2(self.width//2, self.height//2)
self.angle = pygame.Vector2(0, 0)
self.angular_velocity = pygame.Vector2(random.uniform(-0.05, 0.05), random.uniform(-0.05, 0.05))
self.amplitude = pygame.Vector2(random.randint(0, self.width//2), random.randint(0, self.height//2))
# 設定oscillator所在surface的格式為per-pixel alpha
self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
def oscillate(self):
self.angle += self.angular_velocity
def show(self):
# 使用具透明度的白色把oscillator所在的surface清空
self.surface.fill((255, 255, 255, 0))
# oscillator的位置
x = self.amplitude.x*math.sin(self.angle.x)
y = self.amplitude.y*math.sin(self.angle.y)
position = pygame.Vector2(x, y) + self.pivot
# 畫出具有透明度的oscillator
color = (0, 0, 0, 100)
pygame.draw.circle(self.surface, color, position, 20)
pygame.draw.line(self.surface, color, position, self.pivot, 3)
# 把oscillator所在的surface貼到最後要顯示的畫面上
self.screen.blit(self.surface, (0, 0))
# python version 3.10.9
import math
import random
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Example 3.7: Oscillator Objects")
WHITE = (255, 255, 255)
WIDTH, HEIGHT = screen_size = (640, 360)
screen = pygame.display.set_mode(screen_size)
FPS = 60
frame_rate = pygame.time.Clock()
oscillators = [Oscillator() for i in range(10)]
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
for oscillator in oscillators:
oscillator.oscillate()
oscillator.show()
pygame.display.update()
frame_rate.tick(FPS)
Exercise 3.8
class Oscillator:
def __init__(self, joint, leg, angular_velocity, amplitude):
# 取得顯示畫面的大小
self.screen = pygame.display.get_surface()
self.width, self.height = self.screen.get_size()
# 關節位置
self.joint = joint
# 腿長
self.leg = leg
self.angle = 0
self.angular_velocity = angular_velocity
self.amplitude = amplitude
# 設定oscillator所在surface的格式為per-pixel alpha
self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
def oscillate(self):
self.angle += self.angular_velocity
def show(self):
self.surface.fill((255, 255, 255, 0))
# 腳尖位置
x = self.leg
y = self.amplitude*math.sin(self.angle)
position = pygame.Vector2(x, y) + self.joint
color = (0, 0, 0, 100)
pygame.draw.circle(self.surface, color, position, 5)
pygame.draw.line(self.surface, color, position, self.joint, 3)
# 把oscillator所在的surface貼到最後要顯示的畫面上
self.screen.blit(self.surface, (0, 0))
# python version 3.10.9
import math
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Exercise 3.8")
WHITE = (255, 255, 255)
WIDTH, HEIGHT = screen_size = (640, 360)
screen = pygame.display.set_mode(screen_size)
FPS = 60
frame_rate = pygame.time.Clock()
oscillators = []
# 腿長,負號代表左腳,正號代表右腳
legs = [-50, 50]
for leg in legs:
for i in range(10):
joint = pygame.Vector2(320+leg/50, 100+20*i)
angular_velocity = 0.08
amplitude = 10
oscillators.append(Oscillator(joint, leg, angular_velocity, amplitude))
# 讓各oscillator的初始位置不一樣,這樣腳的動作才不會過於整齊劃一,像是在划龍舟一樣
for i in range(20):
oscillators[i].angle = 0.2*i*math.pi
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
for oscillator in oscillators:
oscillator.oscillate()
oscillator.show()
pygame.display.update()
frame_rate.tick(FPS)
Exercise 3.9
class Oscillator:
def __init__(self):
# 取得顯示畫面的大小
self.screen = pygame.display.get_surface()
self.width, self.height = self.screen.get_size()
# 樞軸,拴住oscillator的地方
self.pivot = pygame.Vector2(self.width//2, self.height//2)
# 最高速限
self.top_speed = 0.1
self.angle = pygame.Vector2(0, 0)
self.angular_acceleration = 1.e-4*pygame.Vector2(random.uniform(-1, 1), random.uniform(-1, 1))
self.angular_velocity = pygame.Vector2(0, 0)
self.amplitude = pygame.Vector2(random.randint(0, self.width//2), random.randint(0, self.height//2))
self.surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
def oscillate(self):
self.angular_velocity += self.angular_acceleration
# 限速
if self.angular_velocity.magnitude() > self.top_speed:
self.angular_velocity.scale_to_length(self.top_speed)
self.angle += self.angular_velocity
def show(self):
# 使用具透明度的白色把oscillator所在的surface清空
self.surface.fill((255, 255, 255, 0))
# oscillator的位置
x = self.amplitude.x*math.sin(self.angle.x)
y = self.amplitude.y*math.sin(self.angle.y)
position = pygame.Vector2(x, y) + self.pivot
# 畫出具有透明度的oscillator
color = (0, 0, 0, 100)
pygame.draw.circle(self.surface, color, position, 20)
pygame.draw.line(self.surface, color, position, self.pivot, 3)
# 把oscillator所在的surface貼到最後要顯示的畫面上
self.screen.blit(self.surface, (0, 0))
# python version 3.10.9
import math
import random
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Exercise 3.9")
WHITE = (255, 255, 255)
WIDTH, HEIGHT = screen_size = (640, 360)
screen = pygame.display.set_mode(screen_size)
FPS = 60
frame_rate = pygame.time.Clock()
oscillators = [Oscillator() for i in range(10)]
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
for oscillator in oscillators:
oscillator.oscillate()
oscillator.show()
pygame.display.update()
frame_rate.tick(FPS)