Try   HackMD

乒乓球小遊戲

遊戲畫面

開始畫面

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

準備1秒畫面

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

遊戲中畫面

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

結束畫面

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

板子圖示

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

球圖示

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

啟動pygame、生成視窗和標題

開頭要先init pygame再定義畫面的長寬跟標題
#pygame.init()

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

#pygame.display.set_caption(標題)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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(圖片, 座標)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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()更新螢幕

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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秒

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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左鍵

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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變化量乘上斜率)
判斷球是否撞牆(左右)

撞了就改變球的斜率跟方向

判斷球是否撞板子

撞了就改變球的斜率跟判斷球往上或往下的變數

判斷球是否會碰底
顯示球
更新並顯示計時器
更新螢幕

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →


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秒

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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:
完整程式碼

https://hackmd.io/@dvzBoqsySxa49VKGhJZErA/r1kkrmxVc

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