pygame实践_1

利用python做游戏——pygame_1

之前碰了Qt,winforms,wpf. 主要是想接触一下GUI

我都不知道该干啥了

这个项目是在《Python编程从入门到实践》上摘写的 个人推荐这本书还有同系列的蟒蛇书

后面想写写matplotlib和其他的python库,不过Python这个语言是真的舒服.

pygame介绍

Pygame游戏结构框架

1.创建界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import sys
import pygame
class AlienInvasions:
''''管理游戏资源和行为的类'''
def __init__(self):
'''初始化游戏并创建游戏资源'''
pygame.init()
self.screen = pygame.display.set_mode((1200,800))
pygame.display.set_caption("Alien Invasion")
def run_game(self):
'''开始游戏的主循环'''
while True:
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
#让最近绘制的屏幕可见
pygame.display.flip()


if __name__ == '__main__':
#创建游戏实例并运行
ai = AlienInvasions()
ai.run_game()

image-20210815132045490

pygame.init() 初始化背景设置 让pygame正常工作

pygame.display.set_mode() 创建一个显示窗口,游戏的所有图形元素都将在其中绘制 (1200,800)是一个元组 宽1200像素 高800像素

赋给self.screen的对象是一个Surface.是屏幕的一部分,用于显示游戏元素

激活游戏的动画循环后,每经过一次循环都将自动重绘这个Surface 将用户输入触发的所有变化都反映出来

run_game()包含一个while循环 监视事件

pygame.event.get()返回一个列表 包含它在上一次被调用发生的所有事件

所有键盘和鼠标事件都将倒是这个for循环进行 如果检查到pygame.QUIT则退出pygame.display.flip()将不断更新屏幕,以显示元素的新位置,并且在原来的位置隐藏元素,从而营造平滑的移动效果

1
2
3
#设置屏幕背景颜色
self.bg_color = (230,230,230) #rgb三元组
self.screen.fill(self.bg_color)

image-20210815134354356

fill处理Surface,填充颜色

2.创建设置类

1
2
3
4
5
6
7
8
class Settings:
'''存储游戏中所设置的类'''
def __init__(self):
'''初始化游戏的设置'''
# 屏幕的设置
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230,230,230)

创建setting.py文件 这些变量可用于其他文件访问

这样写从代码上便于分离

1
2
3
4
5
6
from settings import Settings
self.settings = Settings()
self.screen = pygame.display.set_mode(
(self.settings.screen_width,self.settings.screen_height)
)
self.screen.fill(self.settings.bg_color)

3.创建飞船类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pygame
class Ship:
'''管理飞船的类'''
def __init__(self,ai_game):
'''初始化飞船并设置其初始位置'''
self.screen = ai_game.screen()
self.screen_rect = ai_game.screen.get_rect()

# 加载飞船图像并获取其外接矩形
self.image = pygame.image.load('images/rocket.bmp')
self.rect = self.image.get_rect()
#对于每艘新飞船 初始化位置 放在屏幕底部中央
self.rect.midbottom = self.screen_rect.midbottom

def blitme(self):
'''在指定位置绘制飞船'''
self.screen.blit(self.image,self.rect)


我们将屏幕和飞船都当作矩形,因为这样处理比较简单

参数有self,ai_game 后者指向当前AlienInvasion实例引用 访问定义中的游戏资源

rect对象有属性x,y,center,centerx,centery,top,bottom等

blit方法将图像self.rect绘制到指定位置

飞船的移动

主要是利用self.ship.rect.x等属性

将run_game()分为两个部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def run_game(self):
'''开始游戏的主循环'''
while True:
# 监视键盘和鼠标事件
self._check_events()
# 更新屏幕
self.ship.update()
self._update_screen()

'''辅助方法'''
def _check_events(self):
'''响应鼠标和键盘事件'''
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
# 向右移动飞船
# self.ship.rect.x += 10
self.ship.moving_right = True
# if event.type == pygame.K_LEFT:
# self.ship.rect.x -=10
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False

def _update_screen(self):
self.screen.fill(self.settings.bg_color)
# self.screen.fill(self.bg_color)
self.ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()

image-20210815150339152

注意event.key这个东西,用来控制飞船的移动

要实现连续移动可以给一个moving_right的flag控制

只要按下就能连续移动

image-20210815150504599

当然,要将这个东西加入while循环监视

image-20210815150714799

加快飞船速度

image-20210815152117260

image-20210815152142591

将速度改为1.5像素 由于rect.x为int值

将改变后的值赋给self.x 再赋值给self.rect.x 只存储整数部分

但问题不大

限制飞船活动范围

主要利用rect.right等限制

rect.right返回距离飞船外接矩形右边缘的x坐标

1
2
3
4
5
6
7
8
9
def update(self):
'''根据移动标志调整飞船位置'''
if self.moving_right and self.rect.right<self.screen_rect.right:
self.x += self.settings.ship_speed
if self.moving_left and self.rect.left>0:
self.x -= self.settings.ship_speed

# 根据self.x更新rect对象
self.rect.x = self.x

重构事件

1
2
3
4
5
6
7
8
9
10
11
12
13
def _check_keydown_events(self, event):
'''响应按键'''
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True

def _check_keyup_events(self, event):
'''相应松开'''
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
1
2
3
4
5
6
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)

按Q键退出

1
2
3
4
5
6
7
8
def _check_keydown_events(self, event):
'''响应按键'''
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()

全屏

1
2
3
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
# self.settings.screen_width = self.screen.get_rect().width
# self.settings.screen_height = self.screen.get_rect().height

创建子弹类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
'''管理飞船所发射子弹的类'''
def __init__(self,ai_game):
'''在飞船当前位置创建一个子弹对象'''
super.__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
self.color = self.settings.bullet_color

#在(0,0)处创建一个表示子弹的矩形,再设置正确的位置
self.rect = pygame.Rect(0,0,self.settings.bullet_width,
self.settings.bullet_height)
self.rect.midtop = ai_game.ship.rect.midtop

#存储用小数表示的子弹位置
self.y = float(self.rect.y) def update(self):
'''向上移动子弹'''
# 更新表示子弹位置的小数值
self.y -= self.settings.bullet_speed
# 更新表示子弹的rect位置
self.rect.y = self.y

def draw_bullet(self):
'''在屏幕上绘制子弹'''
pygame.draw.rect(self.screen, self.color, self.rect)
def update(self):
'''向上移动子弹'''
# 更新表示子弹位置的小数值
self.y -= self.settings.bullet_speed
# 更新表示子弹的rect位置
self.rect.y = self.y
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  def _fire_bullet(self):
'''创建一颗子弹 将其加入编组中'''
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_screen(self):
# self.screen.fill(self.settings.bg_color)
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
def _check_keydown_events(self, event):
'''响应按键'''
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet() #发射子弹
def run_game(self):
'''开始游戏的主循环'''
while True:
# 监视键盘和鼠标事件
self._check_events()
# 更新屏幕
self.ship.update()
self.bullets.update()#更新位置
self._update_screen()

删除消失的子弹

子弹没有消除,所以会消耗内存

1
2
3
for bullet in self.bullets.copy():
if bullet.rect.bottom <=0:
self.bullets.remove(bullet)

使用copy是因为遍历的数组不能变化

限制子弹数量

settings.py设置

1
self.bullet_allowed = 3
1
2
3
4
5
6
def _fire_bullet(self):
'''创建一颗子弹 将其加入编组中'''
#限制数量 当子弹数量小于限制数量时
if len(self.bullets)<self.settings.bullet_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)

重构代码

1
2
3
4
5
6
7
8
def _updata_bullets(self):
'''更新子弹位置并删除消失子弹'''
self.bullets.update()

# 删除消失的子弹
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
1
2
3
4
5
6
7
8
9
def run_game(self):
'''开始游戏的主循环'''
while True:
# 监视键盘和鼠标事件
self._check_events()
# 更新屏幕
self.ship.update()
self._updata_bullets()
self._update_screen()

创建外星人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
'''表示单个外星人'''
def __init__(self,ai_game):
'''初始化外星人'''
super().__init__()
self.screen = ai_game.screen

#加载外星人图像并设置大小位置
self.image = pygame.image.load('./images/ufo.bmp')
self.rect = self.image.get_rect()

#每个外星人都从左上角产生出
self.rect.x = self.rect.width
self.rect.y = self.rect.height

self.x = float(self.rect.x)

设置其图像与大小,位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def _create_fleet(self):
'''创建外星人群'''
alien = Alien(self)
alien_width = alien.rect.width
available_space_x = self.settings.screen_width - (2 * alien_width)
number_aliens_x = available_space_x // (2 * alien_width)
# 创建一行外星人
for alien_number in range(number_aliens_x):
self._create_alien(alien_number)

def _create_alien(self, alien_number):
# 创建一个外星人
alien = Alien(self)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
self.aliens.add(alien)

在__init__中加入

1
2
self.aliens = pygame.sprite.Group()
self._create_fleet()

同时

1
2
3
4
5
6
7
8
9
def _update_screen(self):
# self.screen.fill(self.settings.bg_color)
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.aliens.draw(self.screen)
# 让最近绘制的屏幕可见
pygame.display.flip()

使用sprits.draw方法

重构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def _create_fleet(self):
'''创建外星人群'''
alien = Alien(self)
alien_width,alien_height = alien.rect.size
available_space_x = self.settings.screen_width - (2 * alien_width)
number_aliens_x = available_space_x // (2 * alien_width)

ship_hight = self.ship.rect.height
available_space_y = (self.settings.screen_height-(3*alien_height)-ship_hight)
number_rows = available_space_y//(2*alien_height)

# 创建外星人群
for row_number in range(number_rows):
for alien_number in range(number_aliens_x):
self._create_alien(alien_number,row_number)

def _create_alien(self, alien_number,row_number):
# 创建一个外星人
alien = Alien(self)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height+2*alien.rect.height*row_number
self.aliens.add(alien)

让外星人移动

1
2
3
4
def update(self):
'''向右移动外星人'''
self.x += self.settings.alien_speed
self.rect.x = self.x

检查边界 到达边界后向另一个方向走并向下走

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def _update_alien(self):
'''更新外星人群中所有外星人的位置'''
self._check_fleet_edges()
self.aliens.update()

def _check_fleet_edges(self):
for alien in self.aliens.sprites():
if alien.check_edges():
self._change_fleet_direction()
break

def _change_fleet_direction(self):
'''将整群外星人下移动 并换左右方向'''
for alien in self.aliens.sprites():
alien.rect.y += self.settings.fleet_drop_speed
self.settings.fleet_direction *= -1
1
2
3
4
5
6
7
8
9
10
def update(self):
'''向右移动外星人'''
self.x += (self.settings.alien_speed * self.settings.fleet_direction)
self.rect.x = self.x

def check_edges(self):
'''如果外星人在边缘返回true'''
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right or self.rect.left <= 0:
return True

射击外星人

设计碰撞

sprits.groupcollide()

1
2
3
4
5
6
7
8
9
def _updata_bullets(self):
'''更新子弹位置并删除消失子弹'''
self.bullets.update()

# 删除消失的子弹
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
self._check_bullet_alien_collisions()
1
2
3
4
5
6
7
8
9
def _check_bullet_alien_collisions(self):
'''相应子弹和外星人碰撞'''
collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
if not self.aliens:
# 删除现有的所有子弹 并创建外星人
self.bullets.empty()
self._create_fleet()


飞船与ufo相碰撞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   def _update_aliens(self):
'''更新外星人群中所有外星人的位置'''
self._check_fleet_edges()
self.aliens.update()
if pygame.sprite.spritecollideany(self.ship,self.aliens):
self._ship_hit()


def _ship_hit(self):
'''响应飞船被外星人碰撞'''
# 将left-1
self.stats.ships_left -= 1
# 清空余下的外星人和子弹
self.aliens.empty()
self.bullets.empty()
# 创建一群新的外星人 并将飞船放到屏幕的底端
self._create_fleet()
self.ship.center_ship()
# 暂停
sleep(0.5)
1
2
3
4
5
6
7
8
9
class GameStats:
'''跟踪游戏统计信息'''
def __init__(self,ai_game):
'''初始化统计信息'''
self.settings = ai_game.settings
self.reset_stats()
def reset_stats(self):
'''初始化在游戏运行期间可能变化的统计信息'''
self.ships_left = self.settings.ship_limit
1
2
3
4
def center_ship(self):
"""让飞船在底端中央"""
self.rect.midbottom = self.screen_rect.midbottom
self.x = float(self.rect.x)

碰撞后飞船消失

检查外星人到屏幕底端

1
2
3
4
5
6
7
8
def _check_aliens_bottom(self):
# 检查外星人是否到达底端
screen_rect = self.screen.get_rect()
for alien in self.aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
# 像飞船被撞到一样处理
self._ship_hit()
break
1
2
3
4
5
6
7
def _update_aliens(self):
'''更新外星人群中所有外星人的位置'''
self._check_fleet_edges()
self.aliens.update()
if pygame.sprite.spritecollideany(self.ship, self.aliens):
self._ship_hit()
self._check_aliens_bottom()

游戏结束

添加一个标志

当数量小于0时设为False

image-20210815201215794

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def _ship_hit(self):
'''响应飞船被外星人碰撞'''
# 将left-1
if self.stats.ships_left>0:
self.stats.ships_left -= 1
# 清空余下的外星人和子弹
self.aliens.empty()
self.bullets.empty()
# 创建一群新的外星人 并将飞船放到屏幕的底端
self._create_fleet()
self.ship.center_ship()
# 暂停
sleep(0.5)
else:
self.stats.game_active = False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def run_game(self):
'''开始游戏的主循环'''
while True:
# 监视键盘和鼠标事件
self._check_events()
# 更新屏幕
if self.stats.game_active:
self.ship.update()
# self.bullets.update()
self._updata_bullets()
self._update_screen()
self._update_aliens()
# for bullet in self.bullets.copy():
# if bullet.rect.bottom <= 0:
# self.bullets.remove(bullet)

先写到这里,后面再补

https://github.com/drowning-in-codes/pygame.git

github地址

-------------本文结束感谢您的阅读-------------
感谢阅读.

欢迎关注我的其它发布渠道