The Nature of Code閱讀心得與Python實作:3.4 Pointing in the...

更新於 2024/08/30閱讀時間約 16 分鐘
這一節的標題是
3.4 Pointing in the Direction of Movement
因為方格子標題字數限制,所以沒完整顯現

在模擬運動中的物體時,如果物體是圓形,那就不需要考慮旋轉的問題,畢竟不管怎麼轉,圓還是圓,看起來都一樣。但是,如果物體不是圓形而是其他形狀呢?模擬如螞蟻、汽車、太空船等不是圓形物體時,除非是要讓它們倒著走,不然不管是直線前進、轉彎,乃至於回頭,都需要讓它們一直面朝運動方向。那要怎麼做,才能讓物體一直面朝運動方向呢?

所謂的「面朝運動方向」,指的其實是「把物體轉到和速度向量一樣的角度」。要達到這個目的,就必須算出速度向量的角度。假設速度向量v=(vx, vy)$,從前一節的圖以及分析可以知道

tanθ = vy / vx

這裡的θ就是速度向量的角度。所以

θ = tan-1(vy / vx)

也就是說,利用tan的反三角函數tan-1,就可以算出v的角度。

在python中,math.atan(x)math.atan2(y, x)都可以用來計算tan-1,但是它們有什麼不同,又該用哪一個呢?

math.atan(x)只有一個參數,算出來的角度範圍介於-π/2到π/2之間。所以,從math.atan(x)所得到的向量角度,沒有辦法得知向量到底是位於哪一個象限。例如,向量(4, 3)和(-4, -3)分別位於第一、三象限,在算角度時,傳給math.atan(x)的數值分別是

3 / 4 = 0.75

(-3) / (-4) = 0.75

這是完全一樣的數值,算出來的角度當然是一樣的。又例如,向量(-4, 3)和(4, -3)分別位於第二、四象限,傳給math.atan(x)的數值分別是

3 / (-4) = -0.75

(-3) / 4 = -0.75

也是完全一樣的數值,最後算出來的角度,當然也會一樣。因此,使用math.atan(x)時,要想知道向量真正的角度,還必須要搭配兩個分量的正負號來判斷才有辦法辦到。

至於math.atan2(y, x),算出的角度範圍,會介於-π到π之間。因此,從math.atan2(y, x)所得到的角度,是向量真正的角度,可以清楚地知道向量到底是位於哪個象限。

從上述的分析就可以知道,選用math.atan2(y, x)來計算向量的角度,是比較好的做法,可以省去不少功夫。

下面這個例子,是把Example 1.10中的mover圖案,由圓形改成長方形,並加入旋轉的功能,讓mover可以自動轉向,永遠面向滑鼠游標的方向。

Example 3.3: Pointing in the Direction of Motion

class Mover:
def __init__(self):
# 取得顯示畫面的大小
self.screen = pygame.display.get_surface()
self.width, self.height = self.screen.get_size()

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

# 最高速限
self.top_speed = 4

# 初始速度
self.velocity = pygame.Vector2(0, 0)

# mover的大小
self.size = (30, 10)

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

def update(self):
# 計算加速度,其方向為,由mover所在位置指向滑鼠游標位置的方向
mouse = pygame.Vector2(pygame.mouse.get_pos())
acceleration = mouse - self.position
# 調整加速度大小,呈現比較好的模擬效果
acceleration.scale_to_length(0.5)

self.velocity += acceleration
# 限速
if self.velocity.length() > self.top_speed:
self.velocity.scale_to_length(self.top_speed)

self.position += self.velocity

def show(self):
# 在surface上畫出mover
rect = pygame.Rect((0, 0), self.size)
pygame.draw.rect(self.surface, (0, 0, 0), rect)

# 讓mover面朝運動方向
angle = math.atan2(self.velocity.y, self.velocity.x)
# 旋轉角度的單位需由弳度轉換為度
rotated_surface = pygame.transform.rotate(self.surface, -math.degrees(angle))
rect_new = rotated_surface.get_rect(center=self.position)

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

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 math
import sys

import pygame # version 2.3.0


pygame.init()

pygame.display.set_caption("Example 3.3: Pointing in the Direction of Motion")

WHITE = (255, 255, 255)

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

FPS = 60
frame_rate = pygame.time.Clock()

mover = Mover()

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

screen.fill(WHITE)

mover.update()
mover.check_edges()
mover.show()

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

在旋轉surface時需加上負號,這是因為surface的原點是在左上角,而y軸的方向是向下,剛好與一般我們所熟悉的,y軸方向向上的直角座標系統相反。所以,必須加上負號來調整,使其方向一致。

Exercise 3.4

按下向左鍵時,加速度向量的方向,是速度向量逆時針轉90度的方向;按下向右鍵時,則是順時針轉90度。寫程式時要注意在畫面上呈現出來的旋轉方向是相反的,必須加上負號來調整。這是因為畫面的y軸方向是向下,跟直角座標系統的y軸方向相反。

class Vehicle:
def __init__(self):
# 取得顯示畫面的大小
self.screen = pygame.display.get_surface()
self.width, self.height = self.screen.get_size()

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

# 最高速限
self.top_speed = 10

# 初始速度
self.velocity = pygame.Vector2(1, 0)

# vehicle的大小
self.size = (30, 10)

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

def update(self):
keys = pygame.key.get_pressed()
# 按鍵盤向左鍵左轉灣;向右鍵右轉灣
if keys[pygame.K_LEFT]:
acceleration = self.velocity.rotate(-90)
# 調整加速度大小,呈現比較好的模擬效果
acceleration.scale_to_length(0.05)
elif keys[pygame.K_RIGHT]:
acceleration = self.velocity.rotate(90)
# 調整加速度大小,呈現比較好的模擬效果
acceleration.scale_to_length(0.05)
else:
acceleration = pygame.Vector2(0, 0)

self.velocity += acceleration
# 限速
if self.velocity.length() > self.top_speed:
self.velocity.scale_to_length(self.top_speed)

self.position += self.velocity

def show(self):
# 在surface上畫出vehicle
rect = pygame.Rect((0, 0), self.size)
pygame.draw.rect(self.surface, (0, 0, 0), rect)

# 讓vehicle面朝運動方向
angle = math.atan2(self.velocity.y, self.velocity.x)
# 旋轉角度的單位需由弳度轉換為度
rotated_surface = pygame.transform.rotate(self.surface, -math.degrees(angle))
rect_new = rotated_surface.get_rect(center=self.position)

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

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 math
import sys

import pygame # version 2.3.0


pygame.init()

pygame.display.set_caption("Exercise 3.4")

WHITE = (255, 255, 255)

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

FPS = 60
frame_rate = pygame.time.Clock()

vehicle = Vehicle()

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

screen.fill(WHITE)

vehicle.update()
vehicle.check_edges()
vehicle.show()

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


avatar-img
15會員
130內容數
寫點東西自娛娛人
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
ysf的沙龍 的其他內容
這一節主要在談三角函數和向量的分量、角度間的關聯性。這個關聯性在向量的應用上,非常重要。
角運動(angular motion)指的是物體的旋轉運動。物體進行直線運動時,可以用速度和加速度來描述它的運動。同樣的做法也可以運用在描述角運動上,我們可以用角速度(angular velocity)和角加速度(angular acceleration)來描述角運動。
角度有兩種單位,一個就是大家耳熟能詳的「度」(degree),記做 °;另一個是「弳度」(radian),也翻譯成「弧度」,記做rad。當使用弳度來度量角度的大小時,通常會把rad省略,不寫出來。
這一章主要在探討,如何模擬震盪(oscillation)相關的物理現象如波、單擺、彈簧等。
到目前為止,我們所模擬的萬有引力,是一個物體吸引另一個物體,或者是一個物體吸引多個物體。然而,在真實世界中,每個物體都會互相吸引,所以在這一節中,就來把模擬的情境,擴展成多個物體互相吸引。
模擬世界是我們寫程式造出來的,我們就是模擬世界的主宰,所以各種作用力要長什麼樣子、要怎麼個作用法,都由我們決定。不過,如果希望這些作用力看起來像真實世界的作用力一樣,那在寫程式的時候,套用這些作用力在真實世界中的物理公式,會是比較省時省力的做法。
這一節主要在談三角函數和向量的分量、角度間的關聯性。這個關聯性在向量的應用上,非常重要。
角運動(angular motion)指的是物體的旋轉運動。物體進行直線運動時,可以用速度和加速度來描述它的運動。同樣的做法也可以運用在描述角運動上,我們可以用角速度(angular velocity)和角加速度(angular acceleration)來描述角運動。
角度有兩種單位,一個就是大家耳熟能詳的「度」(degree),記做 °;另一個是「弳度」(radian),也翻譯成「弧度」,記做rad。當使用弳度來度量角度的大小時,通常會把rad省略,不寫出來。
這一章主要在探討,如何模擬震盪(oscillation)相關的物理現象如波、單擺、彈簧等。
到目前為止,我們所模擬的萬有引力,是一個物體吸引另一個物體,或者是一個物體吸引多個物體。然而,在真實世界中,每個物體都會互相吸引,所以在這一節中,就來把模擬的情境,擴展成多個物體互相吸引。
模擬世界是我們寫程式造出來的,我們就是模擬世界的主宰,所以各種作用力要長什麼樣子、要怎麼個作用法,都由我們決定。不過,如果希望這些作用力看起來像真實世界的作用力一樣,那在寫程式的時候,套用這些作用力在真實世界中的物理公式,會是比較省時省力的做法。
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
到目前為止,我們所模擬的萬有引力,是一個物體吸引另一個物體,或者是一個物體吸引多個物體。然而,在真實世界中,每個物體都會互相吸引,所以在這一節中,就來把模擬的情境,擴展成多個物體互相吸引。
Thumbnail
模擬世界是我們寫程式造出來的,我們就是模擬世界的主宰,所以各種作用力要長什麼樣子、要怎麼個作用法,都由我們決定。不過,如果希望這些作用力看起來像真實世界的作用力一樣,那在寫程式的時候,套用這些作用力在真實世界中的物理公式,會是比較省時省力的做法。
到目前為止,為了簡化問題,我們都假設物體的質量是1。接下來,我們將移除這個假設,然後將完全符合牛頓第二運動定律的apply_force()方法,整合到Mover這個類別中。
這一節談的是牛頓的三大運動定律,以及力對於物體運動狀態的影響。
介紹以物件導向的方式,以向量來實作物體運動的模擬程式。
Thumbnail
這一節談的是向量的定義,以及如何運用向量來建立模擬物體運動時,關於位置和速度間的關係式。
使用向量來處理問題有很多好處,其中一個好處,就是可以減少變數的數量。在這節中,會用一個簡單的例子來介紹,使用向量跟不使用向量,對變數的數量會有什麼樣的影響。
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
到目前為止,我們所模擬的萬有引力,是一個物體吸引另一個物體,或者是一個物體吸引多個物體。然而,在真實世界中,每個物體都會互相吸引,所以在這一節中,就來把模擬的情境,擴展成多個物體互相吸引。
Thumbnail
模擬世界是我們寫程式造出來的,我們就是模擬世界的主宰,所以各種作用力要長什麼樣子、要怎麼個作用法,都由我們決定。不過,如果希望這些作用力看起來像真實世界的作用力一樣,那在寫程式的時候,套用這些作用力在真實世界中的物理公式,會是比較省時省力的做法。
到目前為止,為了簡化問題,我們都假設物體的質量是1。接下來,我們將移除這個假設,然後將完全符合牛頓第二運動定律的apply_force()方法,整合到Mover這個類別中。
這一節談的是牛頓的三大運動定律,以及力對於物體運動狀態的影響。
介紹以物件導向的方式,以向量來實作物體運動的模擬程式。
Thumbnail
這一節談的是向量的定義,以及如何運用向量來建立模擬物體運動時,關於位置和速度間的關係式。
使用向量來處理問題有很多好處,其中一個好處,就是可以減少變數的數量。在這節中,會用一個簡單的例子來介紹,使用向量跟不使用向量,對變數的數量會有什麼樣的影響。