Python100天打卡-Day10-圖形使用者界面和遊戲開發
圖形使用者界面和遊戲開發
基于tkinter子產品的GUI
Python預設的GUI開發子產品是tkinter(在Python 3以前的版本中名為Tkinter)
使用tkinter來開發GUI應用需要以下5個步驟:
導入tkinter子產品中我們需要的東西。
建立一個頂層視窗對象并用它來承載整個GUI應用。
在頂層視窗對象上添加GUI元件。
通過代碼将這些GUI元件的功能組織起來。
進入主事件循環(main loop)。
GUI應用通常是事件驅動式的,之是以要進入主事件循環就是要監聽滑鼠、鍵盤等各種事件的發生并執行對應的代碼對事件進行處理,因為事件會持續的發生,是以需要這樣的一個循環一直運作着等待下一個事件的發生。
另一方面,Tk為控件的擺放提供了三種布局管理器,通過布局管理器可以對控件進行定位,這三種布局管理器分别是:
Placer(開發者提供控件的大小和擺放位置)
Packer(自動将控件填充到合适的位置)
Grid(基于網格坐标來擺放控件)
使用Pygame進行遊戲開發
Pygame是一個開源的Python子產品,專門用于多媒體應用(如電子遊戲)的開發,其中包含對圖像、聲音、視訊、事件、碰撞等的支援。
下面我們來完成一個簡單的小遊戲,遊戲的名字叫“大球吃小球”
制作遊戲視窗
import pygame
def main():
# 初始化導入的pygame中的子產品
pygame.init()
# 初始化用于顯示的視窗并設定視窗尺寸
screen = pygame.display.set_mode((800, 600))
# 設定目前視窗的标題
pygame.display.set_caption('大球吃小球')
running = True
# 開啟一個事件循環處理發生的事件
while running:
# 從消息隊列中擷取事件并對事件進行處理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if name == '__main__':
main()
在視窗中繪圖
可以通過pygame中draw子產品的函數在視窗上繪圖,可以繪制的圖形包括:線條、矩形、多邊形、圓、橢圓、圓弧等。
需要說明的是,螢幕坐标系是将螢幕左上角設定為坐标原點(0, 0),向右是x軸的正向,向下是y軸的正向
在表示位置或者設定尺寸的時候,我們預設的機關都是像素。所謂像素就是螢幕上的一個點,你可以用浏覽圖檔的軟體試着将一張圖檔放大若幹倍,就可以看到這些點。
pygame中表示顔色用的是色光三原色表示法,即通過一個元組或清單來指定顔色的RGB值,每個值都在0~255之間,因為是每種原色都用一個8位(bit)的值來表示,三種顔色相當于一共由24位構成,這也就是常說的“24位顔色表示法”。
# 初始化導入的pygame中的子產品
pygame.init()
# 初始化用于顯示的視窗并設定視窗尺寸
screen = pygame.display.set_mode((800, 600))
# 設定目前視窗的标題
pygame.display.set_caption('大球吃小球')
# 設定視窗的背景色(顔色是由紅綠藍三原色構成的元組)
screen.fill((242, 242, 242))
# 繪制一個圓(參數分别是: 螢幕, 顔色, 圓心位置, 半徑, 0表示填充圓)
pygame.draw.circle(screen, (255, 0, 0,), (100, 100), 30, 0)
# 重新整理目前視窗(渲染視窗将繪制的圖像呈現出來)
pygame.display.flip()
running = True
# 開啟一個事件循環處理發生的事件
while running:
# 從消息隊列中擷取事件并對事件進行處理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
main()
加載圖像
如果需要直接加載圖像到視窗上,可以使用pygame中image子產品的函數來加載圖像,再通過之前獲得的視窗對象的blit方法渲染圖像。
# 初始化導入的pygame中的子產品
pygame.init()
# 初始化用于顯示的視窗并設定視窗尺寸
screen = pygame.display.set_mode((800, 600))
# 設定目前視窗的标題
pygame.display.set_caption('大球吃小球')
# 設定視窗的背景色(顔色是由紅綠藍三原色構成的元組)
screen.fill((255, 255, 255))
# 通過指定的檔案名加載圖像
ball_image = pygame.image.load('./res/ball.png')
# 在視窗上渲染圖像
screen.blit(ball_image, (50, 50))
# 重新整理目前視窗(渲染視窗将繪制的圖像呈現出來)
pygame.display.flip()
running = True
# 開啟一個事件循環處理發生的事件
while running:
# 從消息隊列中擷取事件并對事件進行處理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
main()
實作動畫效果
要實作動畫效果,本身的原理也非常簡單,就是将不連續的圖檔連續的播放,隻要每秒鐘達到了一定的幀數,那麼就可以做出比較流暢的動畫效果。
# 初始化導入的pygame中的子產品
pygame.init()
# 初始化用于顯示的視窗并設定視窗尺寸
screen = pygame.display.set_mode((800, 600))
# 設定目前視窗的标題
pygame.display.set_caption('大球吃小球')
# 定義變量來表示小球在螢幕上的位置
x, y = 50, 50
running = True
# 開啟一個事件循環處理發生的事件
while running:
# 從消息隊列中擷取事件并對事件進行處理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((255, 255, 255))
pygame.draw.circle(screen, (255, 0, 0,), (x, y), 30, 0)
pygame.display.flip()
# 每隔50毫秒就改變小球的位置再重新整理視窗
pygame.time.delay(50)
x, y = x + 5, y + 5
main()
碰撞檢測
pygame的sprite(動畫精靈)子產品就提供了對碰撞檢測的支援,這裡我們暫時不介紹sprite子產品提供的功能,因為要檢測兩個小球有沒有碰撞其實非常簡單,隻需要檢查球心的距離有沒有小于兩個球的半徑之和。
from enum import Enum, unique
from math import sqrt
from random import randint
@unique
class Color(Enum):
"""顔色"""
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (242, 242, 242)
@staticmethod
def random_color():
"""獲得随機顔色"""
r = randint(0, 255)
g = randint(0, 255)
b = randint(0, 255)
return (r, g, b)
class Ball(object):
"""球"""
def __init__(self, x, y, radius, sx, sy, color=Color.RED):
"""初始化方法"""
self.x = x
self.y = y
self.radius = radius
self.sx = sx
self.sy = sy
self.color = color
self.alive = True
def move(self, screen):
"""移動"""
self.x += self.sx
self.y += self.sy
if self.x - self.radius <= 0 or \
self.x + self.radius >= screen.get_width():
self.sx = -self.sx
if self.y - self.radius <= 0 or \
self.y + self.radius >= screen.get_height():
self.sy = -self.sy
def eat(self, other):
"""吃其他球"""
if self.alive and other.alive and self != other:
dx, dy = self.x - other.x, self.y - other.y
distance = sqrt(dx ** 2 + dy ** 2)
if distance < self.radius + other.radius \
and self.radius > other.radius:
other.alive = False
self.radius = self.radius + int(other.radius * 0.146)
def draw(self, screen):
"""在視窗上繪制球"""
pygame.draw.circle(screen, self.color,
(self.x, self.y), self.radius, 0)
事件處理
可以在事件循環中對滑鼠事件進行處理,通過事件對象的type屬性可以判定事件類型,再通過pos屬性就可以獲得滑鼠點選的位置。在點選滑鼠的位置建立顔色、大小和移動速度都随機的小球.
# 定義用來裝所有球的容器
balls = []
# 初始化導入的pygame中的子產品
pygame.init()
# 初始化用于顯示的視窗并設定視窗尺寸
screen = pygame.display.set_mode((800, 600))
# 設定目前視窗的标題
pygame.display.set_caption('大球吃小球')
running = True
# 開啟一個事件循環處理發生的事件
while running:
# 從消息隊列中擷取事件并對事件進行處理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 處理滑鼠事件的代碼
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
# 獲得點選滑鼠的位置
x, y = event.pos
radius = randint(10, 100)
sx, sy = randint(-10, 10), randint(-10, 10)
color = Color.random_color()
# 在點選滑鼠的位置建立一個球(大小、速度和顔色随機)
ball = Ball(x, y, radius, sx, sy, color)
# 将球添加到清單容器中
balls.append(ball)
screen.fill((255, 255, 255))
# 取出容器中的球 如果沒被吃掉就繪制 被吃掉了就移除
for ball in balls:
if ball.alive:
ball.draw(screen)
else:
balls.remove(ball)
pygame.display.flip()
# 每隔50毫秒就改變球的位置再重新整理視窗
pygame.time.delay(50)
for ball in balls:
ball.move(screen)
# 檢查球有沒有吃到其他的球
for other in balls:
ball.eat(other)
main()
其實上面的代碼中還有很多值得改進的地方
比如重新整理視窗以及讓球移動起來的代碼并不應該放在事件循環中,等學習了多線程的知識後,用一個背景線程來處理這些事可能是更好的選擇。
如果希望獲得更好的使用者體驗,我們還可以在遊戲中加入背景音樂以及在球與球發生碰撞時播放音效,利用pygame的mixer和music子產品,我們可以很容易的做到這一點
參考資料: https://github.com/jackfrued/Python-100-Days/blob/master/Day01-15/Day10/ 圖形使用者界面和遊戲開發.md
作者:wxl1999
來源:CSDN
原文:
https://blog.csdn.net/wxl1999/article/details/90300899版權聲明:本文為部落客原創文章,轉載請附上博文連結!