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

閱讀時間約 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)


15會員
129內容數
寫點東西自娛娛人
留言0
查看全部
發表第一個留言支持創作者!
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
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
到目前為止,我們所模擬的萬有引力,是一個物體吸引另一個物體,或者是一個物體吸引多個物體。然而,在真實世界中,每個物體都會互相吸引,所以在這一節中,就來把模擬的情境,擴展成多個物體互相吸引。
Thumbnail
模擬世界是我們寫程式造出來的,我們就是模擬世界的主宰,所以各種作用力要長什麼樣子、要怎麼個作用法,都由我們決定。不過,如果希望這些作用力看起來像真實世界的作用力一樣,那在寫程式的時候,套用這些作用力在真實世界中的物理公式,會是比較省時省力的做法。
到目前為止,為了簡化問題,我們都假設物體的質量是1。接下來,我們將移除這個假設,然後將完全符合牛頓第二運動定律的apply_force()方法,整合到Mover這個類別中。
這一節談的是牛頓的三大運動定律,以及力對於物體運動狀態的影響。
介紹以物件導向的方式,以向量來實作物體運動的模擬程式。
Thumbnail
這一節談的是向量的定義,以及如何運用向量來建立模擬物體運動時,關於位置和速度間的關係式。
使用向量來處理問題有很多好處,其中一個好處,就是可以減少變數的數量。在這節中,會用一個簡單的例子來介紹,使用向量跟不使用向量,對變數的數量會有什麼樣的影響。
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
到目前為止,我們所模擬的萬有引力,是一個物體吸引另一個物體,或者是一個物體吸引多個物體。然而,在真實世界中,每個物體都會互相吸引,所以在這一節中,就來把模擬的情境,擴展成多個物體互相吸引。
Thumbnail
模擬世界是我們寫程式造出來的,我們就是模擬世界的主宰,所以各種作用力要長什麼樣子、要怎麼個作用法,都由我們決定。不過,如果希望這些作用力看起來像真實世界的作用力一樣,那在寫程式的時候,套用這些作用力在真實世界中的物理公式,會是比較省時省力的做法。
到目前為止,為了簡化問題,我們都假設物體的質量是1。接下來,我們將移除這個假設,然後將完全符合牛頓第二運動定律的apply_force()方法,整合到Mover這個類別中。
這一節談的是牛頓的三大運動定律,以及力對於物體運動狀態的影響。
介紹以物件導向的方式,以向量來實作物體運動的模擬程式。
Thumbnail
這一節談的是向量的定義,以及如何運用向量來建立模擬物體運動時,關於位置和速度間的關係式。
使用向量來處理問題有很多好處,其中一個好處,就是可以減少變數的數量。在這節中,會用一個簡單的例子來介紹,使用向量跟不使用向量,對變數的數量會有什麼樣的影響。