這一節的標題是
3.5 Polar vs. Cartesian Coordinates
因為方格子標題字數限制,所以沒完整顯現
到目前為止,我們在畫圖時,使用的都是直角座標系統,也就是笛卡兒座標(Cartesian coordinate)系統。在直角座標系統中,只要給定x、y軸的座標值,我們就可以找到那個點。所以,在直角座標系統中的點,就以(x, y)來表示。
除了直角座標系統外,極座標(polar coordinate)系統是另一種相當有用的座標系統。這種座標系統,使用的是半徑和旋轉角度來定位空間中的點。這裡的半徑,指的是距離原點的距離;而角度指的,則是繞著原點旋轉多大的角度。所以,極座標系統中的點,就以(r, θ)來表示,其中r就是半徑;而θ則是角度。
以向量的觀點來看,直角座標系統的x、y,就是向量的兩個分量;而極座標系統的r、θ,則是向量的大小和方向,也就是長度和角度。
利用三角函數,可以在直角座標和極座標之間進行轉換
在pygame中,也提供了方法,可以在直角座標和極座標之間進行轉換:
由極座標轉成直角座標
vec = pygame.Vector2()
vec.from_polar((r, theta))
或
vec = pygame.Vector2.from_polar((r, theta))
由直角座標轉成極座標
vec = pygame.Vector2(x, y)
(r, theta) = vec.as_polar()
要注意的是,這裡的角度theta
,單位是「度」。
某些問題使用直角座標系統來處理時,會有些難度,但是如果用極座標系統來處理,卻會非常簡單。例如,從下面這個例子就可以看出來,要讓一個圖案繞著一個點做圓周運動,用極座標系統來處理,就會比用直角座標系統來處理,要簡單許多。
Example 3.4: Polar to Cartesian
# python version 3.10.9
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Example 3.4: Polar to Cartesian")
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()
r, theta = 75, 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(WHITE)
# 圓球位置
position = pygame.Vector2.from_polar((r, theta)) + pygame.Vector2(screen_center)
pygame.draw.line(screen, BLACK, screen_center, position, 2)
pygame.draw.circle(screen, BLACK, position, 16)
# 角度的單位是「度」
theta += 1
pygame.display.update()
frame_rate.tick(FPS)
Exercise 3.5
# python version 3.10.9
import sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Exercise 3.5")
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()
r, theta = 0, 0
screen.fill(WHITE)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
position = pygame.Vector2.from_polar((r, theta)) + pygame.Vector2(screen_center)
pygame.draw.circle(screen, BLACK, position, 5)
r += 0.05
theta += 1 # 角度的單位是「度」
pygame.display.update()
frame_rate.tick(FPS)
Exercise 3.6
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
# 起始位置位於畫面中央
self.position = pygame.Vector2(self.width//2, self.height//2)
# 最高速限
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)
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):
# 按下z鍵會產生推力
keys = pygame.key.get_pressed()
if keys[pygame.K_z]:
thrust = pygame.Vector2.from_polar((1, self.heading))
thrust.scale_to_length(0.1)
# 顯示畫面的y軸方向與直角座標系統的y軸方向相反
thrust.y = -thrust.y
else:
# 沒有按下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() > self.top_speed:
self.velocity.scale_to_length(self.top_speed)
elif self.velocity.length() < 1e-5:
self.velocity *= 0
self.position += self.velocity
self.acceleration *= 0
def show(self):
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)
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.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 sys
import pygame # version 2.3.0
pygame.init()
pygame.display.set_caption("Exercise 3.6")
WHITE = (255, 255, 255)
screen_size = (640, 360)
screen = pygame.display.set_mode(screen_size)
FPS = 60
frame_rate = pygame.time.Clock()
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)