
視窗設定成600×800的大小,球台以黑色作為背景,黃色的球,上方灰色的球拍與下方白色的球拍,右方顯示分數。遊戲規則簡單一點,不考慮一局一局的發球回合,球由中央朝隨機方向發出後,到視窗的四個邊緣都會彈回,球拍可以直接把球擊出。如果球落在球拍後方的視窗邊緣,對手就得一分。
我們逐步發展這個程式,從模擬球的移動到將灰色的球拍交給電腦自行控制。
模擬球的移動
我們利用draw模組中的circle()函數畫出球,然後每一次重新繪圖時,球的圓心x座標及y座標各遞增1,如此產生效果彷彿平面上的球在移動,程式碼如下。
#《電腦做什麼事》的範例程式碼 http://pydoing.blogspot.com/
import pygame
from pygame.locals import *
from sys import exit
size = (600, 800)
title = "Pong Test"
black = (0, 0, 0)
yellow = (255, 255, 0)
def move(point, radius, dx, dy):
x, y = point
if x > 0 + radius:
x += dx
if x < 0 + radius:
dx *= -1
x = 0 + radius
if x < size[0] - radius:
x += dx
if x > size[0] - radius:
dx *= -1
x = size[0] - radius
if y < 0 + radius:
y += dy
if y > 0 + radius:
dy *= -1
y = 0 + radius
if y < size[1] - radius:
y += dy
if y > size[1] - radius:
dy *= -1
y = size[1] - radius
return x, y, dx, dy
def run():
pygame.init()
screen = pygame.display.set_mode(size, 0, 32)
pygame.display.set_caption(title)
ball_point = (400, 300)
radius = 10
dx, dy = 1, 1
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.fill(black)
ball = pygame.draw.circle(screen, yellow, ball_point, radius)
x, y, dx, dy = move(ball_point, radius, dx, dy)
ball_point = (x, y)
pygame.display.update()
if __name__ == "__main__":
run()在run()函數中,我們用變數ball_point儲存圓心座標,變數radius儲存圓的半徑,x及y方向的變化量分別用變數dx及dy儲存,進到遊戲的主要迴圈後,第一次繪圖輸出後,每一次重新繪圖前實際計算圓心座標與x、y方向的變化量則交給move()函數處理。
move()函數需要四個參數,分別是圓心座標ball_point、半徑radius以及x及y方向的變化量dx與dy。ball_point在move()函數中會被分別拆成x與y座標,在我們所規劃的球台範圍內,也就是x大於0加上半徑,小於size[0]減掉半徑,y亦同,大於0加上半徑,小於size[1]減掉半徑之中,x遞增dx而y遞增dy。
如果x或y超出這個範圍,dx或dy個別改變正負號,使球往反方向前進。最後,move()函數回傳x、y座標值,以及在正值與負值之間來回跳動的dx與dy,我們實際來看看模擬的效果吧!

隨機方向
當我們把dx與dy都先指派為1時,程式一開始執行,也就是發球的同時,球總是往右下的方向前進,而且每一次執行球的軌跡都一樣。我們可以利用以下的函數,讓發出的球朝隨機的角度。
#《電腦做什麼事》的範例程式碼
#http://pydoing.blogspot.com/
def initial():
if randint(0, 1):
dx = randint(0, 2)
else:
dx = -randint(0, 2)
if randint(0, 1):
dy = randint(1, 2)
else:
dy = -randint(1, 2)
return dx, dy這個函數借助標準模組庫中random模組的randint()函數,所以在程式的開頭不要忘了加入
#《電腦做什麼事》的範例程式碼 #http://pydoing.blogspot.com/ from random import randint
dx的初始值設定為-2、-1、0、1、2其中之一,dy初始值設定為為-2、-1、1、2其中之一,提供發球有二十種可能的方向,若dx初始值為0,球依dy為正或負,直接往垂直方向上或下前進。
而在run()函數內,指派dx、dy初值的陳述要更改如下。
#《電腦做什麼事》的範例程式碼 #http://pydoing.blogspot.com/ dx, dy = initial()
時間因素
但是這樣仍有一些缺點,在我們自己的電腦上看起來也許效果還可以,但是換到其他的電腦上可能會有所不同,尤其是速度較快的電腦與速度較慢的電腦之間的轉移。這樣的的問題主要是每一次重新繪圖,也就是畫面與畫面間的切換,我們並不知道到底經過多少時間。
我們可以利用pygame模組庫中的time模組,控制所經過的時間,然後依比例計算出位移量。
#《電腦做什麼事》的範例程式碼 http://pydoing.blogspot.com/
import pygame
from pygame import *
from sys import exit
from random import randint
size = (600, 800)
title = "Pong Test"
black = (0, 0, 0)
white = (255, 255, 255)
yellow = (255, 255, 0)
def move(point, radius, dx, dy, seconds):
x, y = point
if x < 0 + radius:
x += dx * seconds
if x > 0 + radius:
dx *= -1
x = 0 + radius
if x < size[0] - radius:
x += dx * seconds
if x > size[0] - radius:
dx *= -1
x = size[0] - radius
if y > 0 + radius:
y += dy * seconds
if y < 0 + radius:
dy *= -1
y = 0 + radius
if y < size[1] - radius:
y += dy * seconds
if y > size[1] - radius:
dy *= -1
y = size[1] - radius
return x, y, dx, dy
def initial():
if randint(0, 1):
sx = 1
else:
sx = -1
if randint(0, 1):
sy = 1
else:
sy = -1
return sx, sy
def speed():
return float(randint(50, 100))
def run():
pygame.init()
screen = pygame.display.set_mode(size, 0, 32)
pygame.display.set_caption(title)
point = (300, 400)
radius = 10
sign_x, sign_y = initial()
dx = sign_x * speed()
dy = sign_y * speed()
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.fill(black)
seconds = clock.tick(30) / 1000.0
ball = pygame.draw.circle(screen, yellow, ball_point, radius)
x, y, dx, dy = move(ball_point, radius, dx, dy, seconds)
ball_point = (x, y)
pygame.display.update()
if __name__ == "__main__":
run()在新的程式裡,initial()函數只有回傳+1或-1給sign_x與sign_y兩個變數,然後dx與dy分別為sign_x及sign_y乘上speed()函數。speed()函數只是簡單的利用randint()函數找出一個整數,然後用float()函數使其成為浮點數型態,事實上,speed()函數我們所要設定的是每一秒的位移量,正如其名,英文speed就是中文速度之意。
然後建立一個變數clock,其為Clock型態,用來記錄時間。進入遊戲的主要迴圈,clock使用tick()方法,並用30作為參數,這可以得到每秒畫面更新不超過30次的時間,單位為千分之一秒,所以除以1000.0得到秒數。
一般來說,這會得到1除以30等於0.033秒,然而這個程式不會佔去所有的CPU時間,因為還有作業系統或是其他程式在等待進入CPU,所以0.033是畫面更新的最小秒數。變數seconds獲得每一次畫面更新的時間,因而要作為計算圓心座標move()函數的參數之一。
move()函數中,x或y的位移量是dx或dy乘上seconds,這是由於dx或dy是速度,而seconds是秒數,速度乘以時間就等於每一次畫面更新的位移量。
加入白色球拍
加入白色的球拍很簡單,我們需要在run()函數先設定一些初始值。
#《電腦做什麼事》的範例程式碼 #http://pydoing.blogspot.com/ side = (80, 12) bat_point = (260, 740) bat_speed = 150
變數side用作球拍的長方形Rect物件的邊長,bat_point則是左上角座標,bat_speed則是球拍移動的速度。畫出球拍的程式碼要加入「while True:」迴圈之中,如下。
#《電腦做什麼事》的範例程式碼 #http://pydoing.blogspot.com/ bat = pygame.draw.rect(screen, white, Rect(bat_point, side))
要移動球拍,也就是等同於改變bat_point的值,這裡,我們希望用鍵盤的左右兩個方向鍵進行控制,於是用變數pressed_key記錄從鍵盤按下了什麼鍵。
#《電腦做什麼事》的範例程式碼 #http://pydoing.blogspot.com/ pressed_key = pygame.key.get_pressed()
然後用另一個batcontrol()函數控制移動。
#《電腦做什麼事》的範例程式碼
#http://pydoing.blogspot.com/
def batcontrol(key, point, speed, side, seconds):
x, y = point
if key[K_LEFT]:
x -= speed * seconds
if x < 0:
x = 0
elif key[K_RIGHT]:
x += speed * seconds
if x + side[0] > size[0]:
x = size[0] - side[0]
return x, y總共需要五個參數,我們在遊戲的主要迴圈呼叫如下。
#《電腦做什麼事》的範例程式碼 #http://pydoing.blogspot.com/ bat_point = batcontrol(pressed_key, bat_point, bat_speed, side, seconds)
batcontrol()函數所回傳的就是作為球拍長方形Rect物件的起始座標。這個函數是如何作用的呢,首先將bat_point的值拆成x與y,函數裡只會變動x的值,y值保持不變。如果使用者按下左方向鍵,x會依次在每個畫面遞減bat_speed乘以seconds,這就是球拍向左的位移量,如果x最後遞減小於0,x就設為0,使球拍不會超出球台範圍。
如果使用者按得是右方向鍵,球拍以類似的方式往右移動,如果x的值加上球拍的長度大於球台的最左側,也就是x+side[0]大於size[0],x就設為size[0]-side[0]。
來試看看吧!

用球拍擊球
有沒有發現嘗試用球拍接球,球會直接穿過球拍,好像球從球拍後面漏掉一樣,這是為什麼呢?因為我們的程式是對球跟球拍處理各自的繪圖,因而當球跟球拍的座標重複時,兩者產生的情況變程式重疊顯示,而球的座標一直在改變,導致看起來像是球穿過球拍一樣。
我們如何製造看起像是用球拍擊球的效果呢?Rect物件有內建的colliderect()方法處理兩個Rect物件碰撞的問題,然而我們需要注意,這個colliderect()方法是個布林函數,只會回傳真,及兩個Rect物件相遇,或是假,也就是兩個Rect物件沒有相遇。
不過我們還需要一個rebound()函數,來處理兩個Rect物件碰撞後的情況。
#《電腦做什麼事》的範例程式碼
#http://pydoing.blogspot.com/
def rebound(point, dx, dy, seconds):
x, y = point
if randint(0, 1):
dx *= -1.0
else:
dx *= 1.0
dy *= -1
x += dx * (seconds + 0.1)
y += dy * (seconds + 0.1)
return x, y, dx, dy總共需要四個參數,point為球的圓心座標,其餘則是dx、dy與seconds。在rebound()函數內,二分之一的機率dx會改變正負值,如果dx改變正負值,球就會往球拍的方向反彈,而如果正負值沒有改變,x會持續遞增或遞減,就像到球台邊緣的反彈一般。
最後rebound()函數同move()函數回傳x、y、dx及dy。而在主要的遊戲迴圈內,碰撞需要以下的程式碼處理。
#《電腦做什麼事》的範例程式碼
#http://pydoing.blogspot.com/
if bat.colliderect(ball):
ball_x, ball_y, ball_dx, ball_dy = rebound(ball_point, ball_dx, ball_dy, seconds)
else:
ball_x, ball_y, ball_dx, ball_dy = move(ball_point, radius, ball_dx, ball_dy, seconds)顯示計分
我們要處理計分,在run()函數先加入以下的設定。
#《電腦做什麼事》的範例程式碼
#http://pydoing.blogspot.com/
score_point = (size[0]-text.get_width()-20, size[1]/2)
score = 0
font = pygame.font.SysFont("arial", 32)變數score_point為分數顯示的位置,score累計分數,同樣的,顯示文字就樣先建立Font型態的變數font。
假設接到球就加一分,我們直接在「while True:」迴圈內作分數累計。
#《電腦做什麼事》的範例程式碼
#http://pydoing.blogspot.com/
if bat.colliderect(ball):
ball_x, ball_y, ball_dx, ball_dy = rebound(ball_point, ball_dx, ball_dy, seconds)
score += 1
else:
ball_x, ball_y, ball_dx, ball_dy = move(ball_point, radius, ball_dx, ball_dy, seconds)當然,也要作顯示分數的繪圖工作。
#《電腦做什麼事》的範例程式碼 #http://pydoing.blogspot.com/ text = font.render(str(score), True, white) screen.blit(text, score_point)
來看看效果吧!

加入灰色球拍
加入灰色球拍如同加入白色球拍一樣,主要是把視窗下方的座標改成上方的座標,比較令人煩惱的,就是如何讓電腦自行控制接球。
其實這就如同batcontrol()函數一般的計算座標,我們定義另一個playercontrol()函數來計算灰色球拍的座標。
#《電腦做什麼事》的範例程式碼
#http://pydoing.blogspot.com/
def playercontrol(player_point, ball_point, speed, side, seconds):
player_x, player_y = player_point
ball_x, ball_y = ball_point
if ball_y < 400:
if player_x < ball_x + side[0]:
player_x += speed * seconds
if player_x < 0:
player_x = 0
elif player_x + side[0] > size[0]:
player_x = size[0] - side[0]
if player_x > ball_x:
player_x -= speed * seconds
if player_x < 0:
player_x = 0
elif player_x + side[0] > size[0]:
player_x = size[0] - side[0]
return player_x, player_y五個參數中,player_point為灰色球拍的座標,ball_point則是球的座標,在函數中player_point及ball_point分別被拆成x及y兩個變數。我們假設球通過球台的一半,也就是ball_y大於400時,灰色球拍才進行接球的判斷。
怎麼作判斷的呢?球拍的x座標與球的x座標加上球拍長度比較,如果球拍的x座標小於球的x座標加上球拍長度,這是說球拍現階段在球的左方,因而球拍向右移動,又如果球拍的x座標大於球的x座標加上球拍長度,球拍就要向左移動。
當然我們不希望球拍移動超出球台範圍,所以假如player_x小於0就將player_x設為0,又如果player_x加上side[0]大於size[0],player_x則設為size[0]-size[0]。最後函數回傳的就是調整過的灰色球拍的座標。
至於分數就得交給move()函數處理,這裡我們把move()函數更名為ballcontrol()函數。
#《電腦做什麼事》的範例程式碼
#http://pydoing.blogspot.com/
def ballcontrol(point, radius, dx, dy, seconds, bat_score, player_score):
x, y = point
if x > 0 + radius:
x += dx * seconds
if x < 0 + radius:
dx *= -1
x = 0 + radius
if x < size[0] - radius:
x += dx * seconds
if x > size[0] - radius:
dx *= -1
x = size[0] - radius
if y > 0 + radius:
y += dy * seconds
if y < 0 + radius:
dy *= -1
y = 0 + radius
bat_score += 1
if y < size[1] - radius:
y += dy * seconds
if y > size[1] - radius:
dy *= -1
y = size[1] - radius
player_score += 1
return x, y, dx, dy, bat_score, player_score白色球拍的分數由變數bat_score記錄,灰色球拍則由變數player_score記錄,我們會發現計分的程式控制很簡單,當y小於0加半徑,bat_score遞增1,也就是白色球拍加一分,而當y大於size[1]加半徑,player_score遞增1,由電腦控制的灰色球拍就加了一分。
到這部份為止的程式碼整理如下。
#《電腦做什麼事》的範例程式碼 http://pydoing.blogspot.com/
import pygame
from pygame.locals import *
from sys import exit
from random import randint
size = (600, 800)
title = "Pong Test"
black = (0, 0, 0)
gray = (192, 192, 192)
white = (255, 255, 255)
yello = (255, 255, 0)
def ballcontrol(point, radius, dx, dy, seconds, bat_score, player_score):
x, y = point
if x > 0 + radius:
x += dx * seconds
if x < 0 + radius:
dx *= -1
x = 0 + radius
if x < size[0] - radius:
x += dx * seconds
if x > size[0] - radius:
dx *= -1
x = size[0] - radius
if y > 0 + radius:
y += dy * seconds
if y < 0 + radius:
dy *= -1
y = 0 + radius
bat_score += 1
if y < size[1] - radius:
y += dy * seconds
if y > size[1] - radius:
dy *= -1
y = size[1] - radius
player_score += 1
return x, y, dx, dy, bat_score, player_score
def rebound(point, dx, dy, seconds):
x, y = point
if randint(0, 1):
dx *= -1.0
else:
dx *= 1.0
dy *= -1
x += dx * (seconds + 0.1)
y += dy * (seconds + 0.1)
return x, y, dx, dy
def initial():
if randint(0, 1):
sx = 1
else:
sx = -1
if randint(0, 1):
sy = 1
else:
sy = -1
return sx, sy
def speed():
return float(randint(50, 100))
def batcontrol(key, point, speed, side, seconds):
x, y = point
if key[K_LEFT]:
x -= speed * seconds
if x < 0:
x = 0
elif key[K_RIGHT]:
x += speed * seconds
if x + side[0] > size[0]:
x = size[0] - side[0]
return x, y
def playercontrol(player_point, ball_point, speed, side, seconds):
player_x, player_y = player_point
ball_x, ball_y = ball_point
if ball_y < 400:
if player_x < ball_x + side[0]:
player_x += speed * seconds
if player_x < 0:
player_x = 0
elif player_x + side[0] > size[0]:
player_x = size[0] - side[0]
if player_x > ball_x:
player_x -= speed * seconds
if player_x < 0:
player_x = 0
elif player_x + side[0] > size[0]:
player_x = size[0] - side[0]
return player_x, player_y
def run():
pygame.init()
screen = pygame.display.set_mode(size, 0, 32)
pygame.display.set_caption(title)
ball_point = (300, 400)
ball_x, ball_y = ball_point
radius = 10.
side = (80, 12)
bat_point = (260, 740)
player_point = (260, 60)
bat_speed = 150
sign_x, sign_y = initial()
ball_dx = sign_x * speed()
ball_dy = sign_y * speed()
score_point = (700, 500)
bat_score = 0
player_score = 0
font = pygame.font.SysFont("arial", 32)
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.fill(black)
seconds = clock.tick(30) / 1000.0
ball = pygame.draw.circle(screen, yello, ball_point, radius)
bat = pygame.draw.rect(screen, white, Rect(bat_point, side))
player = pygame.draw.rect(screen, gray, Rect(player_point, side))
player_x, player_y = player_point
bat_text = font.render(str(bat_score), True, white)
player_text = font.render(str(player_score), True, gray)
screen.blit(bat_text, (size[0]-bat_text.get_width()-20, size[1]/2+80))
screen.blit(player_text, (size[0]-player_text.get_width()-20, size[1]/2-80))
# ball control
if bat.colliderect(ball):
ball_x, ball_y, ball_dx, ball_dy = rebound(ball_point, ball_dx, ball_dy, seconds)
elif player.colliderect(ball):
ball_x, ball_y, ball_dx, ball_dy = rebound(ball_point, ball_dx, ball_dy, seconds)
else:
ball_x, ball_y, ball_dx, ball_dy, bat_score, player_score = ballcontrol(ball_point, radius, ball_dx, ball_dy, seconds, bat_score, player_score)
ball_point = (ball_x, ball_y)
# user's bat
pressed_key = pygame.key.get_pressed()
bat_point = batcontrol(pressed_key, bat_point, bat_speed, side, seconds)
# artificial intelligence
player_point = playercontrol(player_point, ball_point, bat_speed, side, seconds)
pygame.display.update()
if __name__ == "__main__":
run()來試看看吧!

加入選單
嗯,灰色球拍有點笨,不過的確會去接球。我們可以試者寫出另一個playercontrol()函數,來控制白色球拍,讓電腦自己跟自己模擬這個乒乓球遊戲。等一等,我們先來作一個進入遊戲的選單程式。
選單程式要怎麼寫呢?所有的程式控制其實有點像我們寫過七巧板程式,顯示選項文字,滑鼠移動到選項文字的上方隨之改變顏色,如果在某個選項上按下滑鼠右鍵,就進行該選項的功能。
我們暫時不考慮點擊滑鼠右鍵的事情,選單程式舉例如下。
#《電腦做什麼事》的範例程式碼 http://pydoing.blogspot.com/
import pygame
from pygame.locals import *
from sys import exit
from random import randint
size = (600, 800)
title = "Pong Game Test"
black = (0, 0, 0)
gray = (128, 128, 128)
white = (255, 255, 255)
yellow = (255, 255, 0)
aqua = (0, 255, 255)
def run():
pygame.init()
screen = pygame.display.set_mode(size, 0, 32)
pygame.display.set_caption(title)
prompt1 = "1. Play"
prompt2 = "2. Simulation"
prompt3 = "3. Exit"
prompt4 = """ """
font1 = pygame.font.SysFont("arial", 40)
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.fill(black)
text1 = font1.render(prompt1, True, white)
text1_1 = font1.render(prompt1, True, aqua)
text2 = font1.render(prompt2, True, white)
text2_1 = font1.render(prompt2, True, aqua)
text3 = font1.render(prompt3, True, white)
text3_1 = font1.render(prompt3, True, aqua)
x, y = pygame.mouse.get_pos()
if 200 <= x <= 200 + text1.get_width() and 200 <= y <= 200 + text1.get_height():
screen.blit(text1_1, (200, 200))
screen.blit(text2, (200, 240))
screen.blit(text3, (200, 280))
elif 200 <= x <= 200 + text2.get_width() and 240 <= y <= 240 + text1.get_height():
screen.blit(text1, (200, 200))
screen.blit(text2_1, (200, 240))
screen.blit(text3, (200, 280))
elif 200 <= x <= 200 + text3.get_width() and 280 <= y <= 280 + text1.get_height():
screen.blit(text1, (200, 200))
screen.blit(text2, (200, 240))
screen.blit(text3_1, (200, 280))
else:
screen.blit(text1, (200, 200))
screen.blit(text2, (200, 240))
screen.blit(text3, (200, 280))
pygame.display.update()
if __name__ == "__main__":
run()執行程式會顯示如下的結果。

如何在按下滑鼠右鍵後進行另一個功能呢?簡單一點的解決方法,把選單程式中的run()函數更名為menu()函數,使用者控制的程式也更名為userplay()函數,這兩個函數都各自包含一個「while True:」迴圈,而在menu()函數裡依需求呼叫userplay()函數,在userplay()函數中按下ESC鍵,就會回到menu()函數,也就是呼叫menu()函數。
我們另外寫一個電腦模擬的simulateplay()函數加入選單,程式碼如下,請自行參考。
#《電腦做什麼事》的範例程式碼 http://pydoing.blogspot.com/
import pygame
from pygame.locals import *
from sys import exit
from random import randint
size = (600, 800)
title = "Pong Game Test"
black = (0, 0, 0)
gray = (128, 128, 128)
white = (255, 255, 255)
yellow = (255, 255, 0)
aqua = (0, 255, 255)
prompt = {1:"1. Play", 2:"2. Simulation", 3:"3. Exit"}
def ballcontrol(point, radius, dx, dy, seconds, bat_score, player_score):
x, y = point
if x > 0 + radius:
x += dx * seconds
if x < 0 + radius:
dx *= -1
x = 0 + radius
if x < size[0] - radius:
x += dx * seconds
if x > size[0] - radius:
dx *= -1
x = size[0] - radius
if y < 0 + radius:
y += dy * seconds
if y > 0 + radius:
dy *= -1
y = 0 + radius
bat_score += 1
if y < size[1] - radius:
y += dy * seconds
if y > size[1] - radius:
dy *= -1
y = size[1] - radius
player_score += 1
return x, y, dx, dy, bat_score, player_score
def rebound(point, dx, dy, seconds):
x, y = point
if randint(0, 1):
dx *= -1.0
else:
dx *= 1.0
dy *= -1
x += dx * (seconds + 0.1)
y += dy * (seconds + 0.1)
return x, y, dx, dy
def initial():
if randint(0, 1):
sx = 1
else:
sx = -1
if randint(0, 1):
sy = 1
else:
sy = -1
return sx, sy
def speed():
return float(randint(50, 100))
def batcontrol(key, point, speed, side, seconds):
x, y = point
if key[K_LEFT]:
x -= speed * seconds
if x < 0:
x = 0
elif key[K_RIGHT]:
x += speed * seconds
if x + side[0] < size[0]:
x = size[0] - side[0]
return x, y
def player1control(player_point, ball_point, speed, side, seconds):
player_x, player_y = player_point
ball_x, ball_y = ball_point
if ball_y < 400:
if player_x < ball_x + side[0]:
player_x += speed * seconds
if player_x < 0:
player_x = 0
elif player_x + side[0] > size[0]:
player_x = size[0] - side[0]
if player_x > ball_x:
player_x -= speed * seconds
if player_x < 0:
player_x = 0
elif player_x + side[0] > size[0]:
player_x = size[0] - side[0]
return player_x, player_y
def player2control(player_point, ball_point, speed, side, seconds):
player_x, player_y = player_point
ball_x, ball_y = ball_point
if ball_y > 400:
if player_x < ball_x + side[0]:
player_x += speed * seconds
if player_x < 0:
player_x = 0
elif player_x + side[0] > size[0]:
player_x = size[0] - side[0]
if player_x > ball_x:
player_x -= speed * seconds
if player_x < 0:
player_x = 0
elif player_x + side[0] > size[0]:
player_x = size[0] - side[0]
return player_x, player_y
def userplay(screen):
ball_point = (300, 400)
ball_x, ball_y = ball_point
radius = 10.
side = (80, 12)
bat_point = (260, 740)
player_point = (260, 60)
bat_speed = 150
sign_x, sign_y = initial()
ball_dx = sign_x * speed()
ball_dy = sign_y * speed()
score_point = (700, 500)
bat_score = 0
player_score = 0
font = pygame.font.SysFont("arial", 32)
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.fill(black)
seconds = clock.tick(30) / 1000.0
ball = pygame.draw.circle(screen, yellow, ball_point, radius)
bat = pygame.draw.rect(screen, white, Rect(bat_point, side))
player = pygame.draw.rect(screen, gray, Rect(player_point, side))
player_x, player_y = player_point
bat_text = font.render(str(bat_score), True, white)
player_text = font.render(str(player_score), True, gray)
screen.blit(bat_text, (size[0]-bat_text.get_width()-20, size[1]/2+80))
screen.blit(player_text, (size[0]-player_text.get_width()-20, size[1]/2-80))
# ball control
if bat.colliderect(ball):
ball_x, ball_y, ball_dx, ball_dy = rebound(ball_point, ball_dx, ball_dy, seconds)
elif player.colliderect(ball):
ball_x, ball_y, ball_dx, ball_dy = rebound(ball_point, ball_dx, ball_dy, seconds)
else:
ball_x, ball_y, ball_dx, ball_dy, bat_score, player_score = ballcontrol(ball_point, radius, ball_dx, ball_dy, seconds, bat_score, player_score)
ball_point = (ball_x, ball_y)
# user's bat
pressed_key = pygame.key.get_pressed()
bat_point = batcontrol(pressed_key, bat_point, bat_speed, side, seconds)
# artificial intelligence
player_point = player1control(player_point, ball_point, bat_speed, side, seconds)
if pygame.key.get_pressed()[K_ESCAPE]:
menu(screen, prompt)
pygame.display.update()
def simulateplay(screen):
ball_point = (300, 400)
ball_x, ball_y = ball_point
radius = 10.
side = (80, 12)
bat_point = (260, 740)
player_point = (260, 60)
bat_speed = 150
sign_x, sign_y = initial()
ball_dx = sign_x * speed()
ball_dy = sign_y * speed()
score_point = (700, 500)
bat_score = 0
player_score = 0
font = pygame.font.SysFont("arial", 32)
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.fill(black)
seconds = clock.tick(30) / 1000.0
ball = pygame.draw.circle(screen, yellow, ball_point, radius)
bat = pygame.draw.rect(screen, white, Rect(bat_point, side))
player = pygame.draw.rect(screen, gray, Rect(player_point, side))
player_x, player_y = player_point
bat_text = font.render(str(bat_score), True, white)
player_text = font.render(str(player_score), True, gray)
screen.blit(bat_text, (size[0]-bat_text.get_width()-20, size[1]/2+80))
screen.blit(player_text, (size[0]-player_text.get_width()-20, size[1]/2-80))
# ball control
if bat.colliderect(ball):
ball_x, ball_y, ball_dx, ball_dy = rebound(ball_point, ball_dx, ball_dy, seconds)
elif player.colliderect(ball):
ball_x, ball_y, ball_dx, ball_dy = rebound(ball_point, ball_dx, ball_dy, seconds)
else:
ball_x, ball_y, ball_dx, ball_dy, bat_score, player_score = ballcontrol(ball_point, radius, ball_dx, ball_dy, seconds, bat_score, player_score)
ball_point = (ball_x, ball_y)
# artificial intelligence
player_point = player1control(player_point, ball_point, bat_speed, side, seconds)
bat_point = player2control(bat_point, ball_point, bat_speed, side, seconds)
if pygame.key.get_pressed()[K_ESCAPE]:
menu(screen, prompt)
pygame.display.update()
def menu(screen, prompt):
font = pygame.font.SysFont("arial", 40)
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
text1 = font.render(prompt[1], True, white)
text1_1 = font.render(prompt[1], True, aqua)
text2 = font.render(prompt[2], True, white)
text2_1 = font.render(prompt[2], True, aqua)
text3 = font.render(prompt[3], True, white)
text3_1 = font.render(prompt[3], True, aqua)
x, y = pygame.mouse.get_pos()
screen.fill(black)
if 200 <= x <= 200 + text1.get_width() and 200 <= y <= 200 + text1.get_height():
screen.blit(text1_1, (200, 200))
screen.blit(text2, (200, 240))
screen.blit(text3, (200, 280))
if pygame.mouse.get_pressed()[0]:
userplay(screen)
elif 200 <= x <= 200 + text2.get_width() and 240 <= y <= 240 + text1.get_height():
screen.blit(text1, (200, 200))
screen.blit(text2_1, (200, 240))
screen.blit(text3, (200, 280))
if pygame.mouse.get_pressed()[0]:
simulateplay(screen)
elif 200 <= x <= 200 + text3.get_width() and 280 <= y <= 280 + text1.get_height():
screen.blit(text1, (200, 200))
screen.blit(text2, (200, 240))
screen.blit(text3_1, (200, 280))
if pygame.mouse.get_pressed()[0]:
exit()
else:
screen.blit(text1, (200, 200))
screen.blit(text2, (200, 240))
screen.blit(text3, (200, 280))
pygame.display.update()
def run():
pygame.init()
screen = pygame.display.set_mode(size, 0, 32)
pygame.display.set_caption(title)
menu(screen, prompt)
if __name__ == "__main__":
run()