---
# System prepended metadata

title: 乒乓球小遊戲

---

# 乒乓球小遊戲
## 遊戲畫面
### 開始畫面
![](https://i.imgur.com/HMKokER.png)

### 準備1秒畫面
![](https://i.imgur.com/DyG6VO8.png)

### 遊戲中畫面
![](https://i.imgur.com/yc8WU6D.png)

### 結束畫面
![](https://i.imgur.com/83g2uZf.png)


### 板子圖示
![](https://i.imgur.com/WxBOgM1.png)

### 球圖示
![](https://i.imgur.com/7olVxcY.png)

## 啟動pygame、生成視窗和標題
開頭要先init pygame再定義畫面的長寬跟標題
#pygame.init()

#window = pygame.display.set_mode((寬, 長))

#pygame.display.set_caption(標題)
![](https://i.imgur.com/8mAQmF2.png)

```python=
import time 
import pygame
import random


pygame.init()

window_height = 600
window_width = 1000
window = pygame.display.set_mode((window_width, window_height))

pygame.display.set_caption("Ping Pong")
```

## 因為等等要建立上下兩個桌球拍，因此要先建立桌球拍的class以增加方便性和可讀性 
class內先設定長、寬、速度、圖示再定義建構子和需要的函式(移動, 顯示)
建構子內要設定座標跟方向
移動函式內要將桌球拍的座標加上方向乘上速度再顯示在window上，且要判斷是否撞牆
顯示函式內就只要將桌球拍顯示在window上
#image =pygame.image.load(圖片位置)

#image = pygame.transform.scale(圖片, (寬, 長))

#image.convert()

#window.blit(圖片, 座標)
![](https://i.imgur.com/nwLppBk.png)

```python=
class Paddle:
    height = window_height // 20
    width = window_height // 3
    speed = 0.7
    paddle_image = pygame.image.load("Resources/paddle.png")
    paddle_image = pygame.transform.scale(paddle_image, (width, height))
    paddle_image.convert()

    def __init__(self, x, y, direction):
        self.x = x
        
        


    def move(self):
        if self.x <= 0 and self.direction == -1:
            self.direction = 0
            self.x = 0
        if  :
            self.direction = 0
            self.x = window_width - Paddle.width
        self.x += self.direction * Paddle.speed
	  window.blit( )
        
    def update_position(self):
        window.blit( )
```

## 定義顏色的rgb, 宣告桌球拍
paddle = Paddle(x, y, direction)
![](https://i.imgur.com/nT4R1Aj.png)

```python=
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

user = Paddle((window_width - Paddle.width) // 2, window_height - Paddle.height - 5, 0)
user2 = Paddle((window_width - Paddle.width) // 2, 5, 0)
```

## 定義球的半徑, x, y, 速度, 方向(x), 斜率(y/x), 圖示
半徑設為相對大小
x、y、方向、斜率設為隨機
速度設為固定大小
之後設定圖示
#random.randint(a, b)
![](https://i.imgur.com/mrTNRs8.png)

```python=
ball_radius = Paddle.width // 5
ball_x = random.randint(2 * Paddle.width, window_width - 2 * Paddle.width)
ball_y = random.randint(Paddle.height + 5, window_height - Paddle.height - 5 - ball_radius)
ball_speed = 0.4
ball_direction = (random.randint(1, 2) - 1.5) * 2
ball_slope = random.randint(7, 15) * 0.1 * ball_direction
ball_image = pygame.image.load("Resources/ball.png")
ball_image = pygame.transform.scale(ball_image, (ball_radius, ball_radius))
ball_image.convert()
```

## 因為下一步要顯示文字，因此先建立文字class
內容包含建構子、顯示函式
建構子內要設定文字內容、文字大小、顏色、座標
 >先將字型跟輸入的數據丟進變數內
 >之後再個別設定將文字框的正中、中上、中下設在宣告的座標(正中已經在圖片內，中上跟中下就如法炮製就好)
 >
顯示函式包含顯示在正中、中上、中下、左上(正常)
#position_center = surface.get_rect()
#position_center.center = position
![](https://i.imgur.com/Qd5SNeO.png)

```python=
class Text:
    def __init__(self, text, size, color, position):
        self.font = pygame.font.SysFont('impact', size)
        self.surface = self.font.render(text, True, color)
        self.position = position
        self.position_center = self.surface.get_rect()
        self.position_center.center = position
        
        
        
        

    def show_center(self):
        window.blit(self.surface, self.position_center)

    def show_midtop(self):
        

    def show_midbottom(self):
        

    def show(self):
        window.blit(self.surface, self.position)
```

## 透過左右鍵判斷pvp or pve
再回圈內要先填滿畫面為白色(覆蓋上一幀)
再定義並顯示文字，一個在中上顯示，一個在中下顯示
顯示完後更新畫面
然後判斷滑鼠左右鍵是否被按下
#window.fill(顏色)

#text = Text(text, size, color, position)

#if mouse_pressed[0~2]:
#....statement

#pygame.display.update()更新螢幕
![](https://i.imgur.com/HMKokER.png)
![](https://i.imgur.com/O2gS5vQ.png)

```python=
while True:
    window.fill(WHITE)
    text_begin_1 = Text("left-click:PVE", 100, BLACK, (window_width // 2, window_height // 2))
    
    
    text_begin_2.show_midtop()
    pygame.display.update()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
    mouse_pressed = pygame.mouse.get_pressed()
   
    
    
    
    
    
```

## 為了給玩家緩衝時間，要先顯示初始畫面1秒再開始
要先填滿畫面為白色(覆蓋上一幀)
再顯示桌球拍跟球
再更新螢幕
之後等待1秒
![](https://i.imgur.com/DyG6VO8.png)
![](https://i.imgur.com/ZOSd7Zv.png)

```python=

user.update_position()
user2.update_position()
window.blit(ball_image, (ball_x, ball_y))

time.sleep(1)
```

## 初始化、計時開始, 進入迴圈, 判斷板子方向並移動板子
初始化球的落點(上方如果是電腦要由此判斷該怎麼移動桌球拍)
初始化判斷是否為球正在往上跑的變數(在判斷落點時需要)
計時開始
進入迴圈
要先填滿畫面為白色(覆蓋上一幀)
判斷是否離開遊戲及板子方向(左右、ad or 球落點)
 >沒按鍵被按著時使桌球拍不要動
 >左右鍵控制下桌球拍
 >ad鍵控制上桌球拍，如果是pvp的話
 >如果是pve則先判斷球的落點
 >>如果球往上跑則落點為現在的x加上(y離桌球拍的距離除斜率)，如果超出邊界則修正
 >>如果往下跑則因為不好寫，因此先設在正中間
 >>之後桌球拍的方向就是看現在落點跟桌球拍的位置做決定
 >
移動板子
#timer = time.time() 獲取現在時間

#pygame.K_RIGHT右鍵
#pygame.K_LEFT左鍵
![](https://i.imgur.com/yc8WU6D.png)

![](https://i.imgur.com/gxvjeeO.png)

![](https://i.imgur.com/lV9ukAW.png)

```python=
collision_x = window_width // 2 - ball_radius // 2
user2_turn = False
begin = t

while running:
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
        elif event.type == pygame.KEYUP:
            
            
            break

    keys_pressed = pygame.key.get_pressed()
    if 
        user.direction = 1
    if 
        user.direction = -1

    if 
        if keys_pressed[pygame.K_a]:
            
        if keys_pressed[pygame.K_d]:
            
    else:
        if user2_turn:
            collision_x = ball_x - (ball_y - user2.y - Paddle.height) // 
            while True:
                if 0 <= collision_x <= window_width - ball_radius:
                    break
                if collision_x >= window_width - ball_radius:
                    collision_x = 
                elif collision_x <= 0:
                    collision_x = 
        else:
            collision_x = window_width // 2 - ball_radius // 2

        if
            user2.direction = 1
        elif 
            user2.direction = -1
        else:
            user2.direction = 0

    user2.move()
    user.move()
```

## 移動球, 判斷球是否撞牆、撞板子、碰底, 顯示球, 更新並顯示計時器, 更新螢幕
移動球(x加上方向乘上速度, y加上x變化量乘上斜率)
判斷球是否撞牆(左右)
 >撞了就改變球的斜率跟方向
 >
判斷球是否撞板子
 >撞了就改變球的斜率跟判斷球往上或往下的變數
 >
判斷球是否會碰底
顯示球
更新並顯示計時器
更新螢幕
![](https://i.imgur.com/yc8WU6D.png)
![](https://i.imgur.com/z9Y1Esr.png)
![](https://i.imgur.com/zkCC6c7.png)

```python=
    ball_x += ball_direction * ball_speed
    ball_y += ball_direction * ball_speed * 

    if ball_x <= 0 or ball_x >= window_width - ball_radius:
        
        

    if ball_y + ball_radius >= user.y:
        if user.x <= ball_x + ball_radius // 2 <= user.x + Paddle.width:
            
            user2_turn = 
    if ball_y <= user2.y + Paddle.height:
        if user2.x <= ball_x + ball_radius // 2 <= user2.x + Paddle.width:
            
            user2_turn = 

    if ball_y + ball_radius > user.y + abs(ball_speed * ball_slope) or ball_y < user2.y + Paddle.height - abs(
            ball_speed * ball_slope):
        break

    window.blit( )

    
    score = Text("Time " + str(time_taken) + "s", 50, BLACK, (0, window_height // 10))
    

    
```

## 結束計時, 顯示存活秒數直到滑鼠或鍵盤有動作後再等一秒在關
先計算過了多久
進入迴圈
(因為畫面不變因此不需要塗白螢幕)
顯示桌球拍跟球和分數(經過時間)
更新螢幕
如果接收到動作則等1秒後離開
#time.sleep(x)等待x秒
![](https://i.imgur.com/83g2uZf.png)
![](https://i.imgur.com/iaPwVpT.png)

```python=
time_taken = int( )
while True:
    user.update_position()
    user2.update_position()
    window.blit(ball_image, (ball_x, ball_y))
    score = Text("Time " + str(time_taken) + "s", 100, BLACK, (window_width // 2, window_height // 2))
    score.show_center()
    
    if len(pygame.event.get()) != 0:
        
        
```

:::spoiler 完整程式碼
https://hackmd.io/@dvzBoqsySxa49VKGhJZErA/r1kkrmxVc
```python=
import time 
import pygame
import random

pygame.init()

window_height = 600
window_width = 1000
window = pygame.display.set_mode((window_width, window_height))

pygame.display.set_caption("Ping Pong")


class Paddle:
    height = window_height // 20
    width = window_height // 3
    speed = 0.7
    paddle_image = pygame.image.load("Resources/paddle.png")
    paddle_image = pygame.transform.scale(paddle_image, (width, height))
    paddle_image.convert()

    def __init__(self, x, y, direction):
        self.x = x
        self.y = y
        self.direction = direction


    def move(self):
        if self.x <= 0 and self.direction == -1:
            self.direction = 0
            self.x = 0
        if self.x >= window_width - Paddle.width and self.direction == 1:
            self.direction = 0
            self.x = window_width - Paddle.width
        self.x += self.direction * Paddle.speed
	  window.blit(self.paddle_image, (self.x, self.y))
        
    def update_position(self):
        window.blit(self.paddle_image, (self.x, self.y))
     
        
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

user = Paddle((window_width - Paddle.width) // 2, window_height - Paddle.height - 5, 0)
user2 = Paddle((window_width - Paddle.width) // 2, 5, 0)

ball_radius = Paddle.width // 5
ball_x = random.randint(2 * Paddle.width, window_width - 2 * Paddle.width)
ball_y = random.randint(Paddle.height + 5, window_height - Paddle.height - 5 - ball_radius)
ball_speed = 0.4
ball_direction = (random.randint(1, 2) - 1.5) * 2
ball_slope = random.randint(7, 15) * 0.1 * ball_direction
ball_image = pygame.image.load("Resources/ball.png")
ball_image = pygame.transform.scale(ball_image, (ball_radius, ball_radius))
ball_image.convert()


class Text:
    def __init__(self, text, size, color, position):
        self.font = pygame.font.SysFont('impact', size)
        self.surface = self.font.render(text, True, color)
        self.position = position
        self.position_center = self.surface.get_rect()
        self.position_center.center = position
        self.position_midtop = self.surface.get_rect()
        self.position_midtop.midtop = position
        self.position_midbottom = self.surface.get_rect()
        self.position_midbottom.midbottom = position

    def show_center(self):
        window.blit(self.surface, self.position_center)

    def show_midtop(self):
        window.blit(self.surface, self.position_midtop)

    def show_midbottom(self):
        window.blit(self.surface, self.position_midbottom)

    def show(self):
        window.blit(self.surface, self.position)
        while True:
     
            
window.fill(WHITE)
text_begin_1 = Text("left-click:PVE", 100, BLACK, (window_width // 2, window_height // 2))
text_begin_2 = Text("right-click:PVP", 100, BLACK, (window_width // 2, window_height // 2))
text_begin_1.show_midbottom()
text_begin_2.show_midtop()
pygame.display.update()

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        quit()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]:
    PVP_bool = False
    break
elif mouse_pressed[2]:
    PVP_bool = True
    break
    
window.fill(WHITE)
user.update_position()
user2.update_position()
window.blit(ball_image, (ball_x, ball_y))
pygame.display.update()
time.sleep(1)

collision_x = window_width // 2 - ball_radius // 2
user2_turn = False
begin = time.time()
running = True
while running:
    window.fill(WHITE)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
        elif event.type == pygame.KEYUP:
            user.direction = 0
            user2.direction = 0
            break

    keys_pressed = pygame.key.get_pressed()
    if keys_pressed[pygame.K_RIGHT]:
        user.direction = 1
    if keys_pressed[pygame.K_LEFT]:
        user.direction = -1

    if PVP_bool:
        if keys_pressed[pygame.K_a]:
            user2.direction = -1
        if keys_pressed[pygame.K_d]:
            user2.direction = 1
    else:
        if user2_turn:
            collision_x = ball_x - (ball_y - user2.y - Paddle.height) // ball_slope
            while True:
                if 0 <= collision_x <= window_width - ball_radius:
                    break
                if collision_x >= window_width - ball_radius:
                    collision_x = 2 * (window_width - ball_radius) - collision_x
                elif collision_x <= 0:
                    collision_x = -1 * collision_x
        else:
            collision_x = window_width // 2 - ball_radius // 2

        if user2.x + Paddle.width <= collision_x + ball_radius:
            user2.direction = 1
        elif user2.x >= collision_x:
            user2.direction = -1
        else:
            user2.direction = 0

    user2.move()
    user.move()
    
    ball_x += ball_direction * ball_speed
    ball_y += ball_direction * ball_speed * ball_slope

    if ball_x <= 0 or ball_x >= window_width - ball_radius:
        ball_direction *= -1
        ball_slope = -1 * ball_slope

    if ball_y + ball_radius >= user.y:
        if user.x <= ball_x + ball_radius // 2 <= user.x + Paddle.width:
            ball_slope = -1 * ball_slope
            user2_turn = True
    if ball_y <= user2.y + Paddle.height:
        if user2.x <= ball_x + ball_radius // 2 <= user2.x + Paddle.width:
            ball_slope = -1 * ball_slope
            user2_turn = False

    if ball_y + ball_radius > user.y + abs(ball_speed * ball_slope) or ball_y < user2.y + Paddle.height - abs(
            ball_speed * ball_slope):
        break

    window.blit(ball_image, (ball_x, ball_y))

    time_taken = int(time.time() - begin)
    score = Text("Time " + str(time_taken) + "s", 50, BLACK, (0, window_height // 10))
    score.show()

    pygame.display.update()
    
time_taken = int(time.time() - begin)
while True:
    user.update_position()
    user2.update_position()
    window.blit(ball_image, (ball_x, ball_y))
    score = Text("Time " + str(time_taken) + "s", 100, BLACK, (window_width // 2, window_height // 2))
    score.show_center()
    pygame.display.update()
    if len(pygame.event.get()) != 0:
        time.sleep(1)
        break
```