# Python班
## 5/31社課
---
### 本次課程內容
* 簡易三角函數
* 建立多個相同物件
* 角色-群組碰撞
* 角色-角色碰撞
---
## 前置作業
在今天的點名文字檔裡,有附上程式的半成品,請複製到replit上面。
----
半成品
```=
import pygame, sys
from pygame.locals import QUIT
pygame.init()
FPS = 30
WIDTH, HEIGHT = 400, 300
global deltax, deltay
v = 6
DISPLAYSURF = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Hello World!')
clock = pygame.time.Clock()
class Block(pygame.sprite.Sprite):
def __init__(self,x):
super().__init__()
self.image = pygame.Surface(((WIDTH - 50)/7, 40))
self.image.fill((0, 255, 0))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.bottom = 50
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 30))
self.image.fill((255,255,255))
pygame.draw.circle(self.image, (255,0,0), (15,15), 15)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH/2
self.rect.centery = HEIGHT/2
self.vx = 0
self.vy = 6
def update(self):
self.rect.x += self.vx
self.rect.y += self.vy
if self.rect.left < 0 or self.rect.right > WIDTH:
self.vx = -self.vx
if self.rect.top < 0:
self.vy = -self.vy
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((70, 10))
self.image.fill((0,0,255))
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH/2
self.rect.bottom = 250
self.v = 8
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= self.v
if keys[pygame.K_RIGHT]:
self.rect.x += self.v
all_sprite = pygame.sprite.Group()
balls = pygame.sprite.Group()
blocks = pygame.sprite.Group()
ball = Ball()
balls.add(ball)
player = Player()
all_sprite.add(ball)
all_sprite.add(player)
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == QUIT:
running = False
sys.exit()
all_sprite.update()
DISPLAYSURF.fill((255,255,255))
all_sprite.draw(DISPLAYSURF)
pygame.display.update()
pygame.quit()
```
---
### 反彈後的速度
---
#### 目標
我們預期的最終的結果
要像一般的打磚塊那樣
反彈方向由接觸點決定
----
### 開始囉
反彈與位置有關,因此碰撞時得確認兩者的位置
所以先寫出一個判斷碰撞與否的程式
然後計算兩者位置之差,以便計算反彈後速度
```python=
...
all_sprite.update()
if pygame.sprite.collide_rect(ball, player):
deltax = ball.rect.centerx - player.rect.centerx
deltay = ball.rect.centery - player.rect.centery
ball.col()
```
----
有個$\texttt{ball.col()}$要幹嘛咧?
就是去計算反彈後的速度
----
我們希望速度是定值,只有方向上的改變
因此我們用到一些簡單的三角函數概念
我們來到$\texttt{ball}$底下,定義$\texttt{col}$
```=
def col(self):
self.vx = v*(deltax/((deltax**2+deltay**2)**0.5))
self.vy = v*(deltay/((deltax**2+deltay**2)**0.5))
```
---
## 打方塊
----
已經幫你們定義好了$\texttt{block}$
仔細看,在$\texttt{__init__}$的括弧中,除了$\texttt{self}$之外還有一個$\texttt{x}$,這是用來定義生成時的位置的。
```=
class Block(pygame.sprite.Sprite):
def __init__(self,x):
super().__init__()
self.image = pygame.Surface(((WIDTH - 50)/7, 40))
self.image.fill((0, 255, 0))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.bottom = 50
```
----
接著開始建立方塊
----
首先,如果這些方塊生來就是要被打掉,不在意誰是誰的話,我們可以用一個$\texttt{for}$迴圈大量製造
```=
for i in range(7):
block = Block(i*(WIDTH/7))
blocks.add(block)
all_sprite.add(block)
```
這裡之所以多開一個$\texttt{blocks}$是為了等等的判斷
----
來到遊戲進行區,我們要來讓球碰到方塊
```=
hit = pygame.sprite.spritecollide(ball, blocks, True)
if hit:
ball.hit()
```
這裡用的是角色-群組碰撞,凡涉及「群組」的碰撞,通常代表不確定碰撞者是誰,所以可以在後面設定布林值來決定碰撞後是否刪除屬於群組的物件
這裡因為方塊要刪掉所以用$\texttt{True}$
----
最後是打到磚塊的反彈
因為我們是極簡版,只有一層磚塊,
所以判斷上比較簡單
我們需要判斷是從側面撞擊還是正面撞擊
----
我們可以在判斷前往回推一步,看看碰撞前一刻球在哪裡
```
def hit(self):
self.rect.x -= self.vx
self.rect.y -= self.vy
...
```
----
接著就可以用碰撞前的位置判斷是碰到左右邊還是下邊
```=
def hit(self):
self.rect.x -= self.vx
self.rect.y -= self.vy
if self.rect.top >= 50:
self.vy = -self.vy
else:
self.vx = -self.vx
```
---
# 完成啦
---
# 謝謝大家
{"title":"112-2-Pygame-極簡版打磚塊實作","contributors":"[{\"id\":\"084e105f-92be-4605-b399-8d3c0ef40c64\",\"add\":6170,\"del\":2186}]","description":"簡易三角函數"}