Python之pygame模块(太空生存小游戏)学习
太空生存小游戏
pygame官方教程链接
1.构建整体框架
import pygame
pygame.init()
WIDTH = 500
HEIGHT = 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("太空生存")
clock = pygame.time.Clock()
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((0, 0, 0))
pygame.quit()
运行结果:
-
pygame.init()-------初始化pygame模块 -
pygame.quit()-------退出pygame模块 -
pygame.display.set_mode() -------创建视窗 -
pygame.display.set_caption()-------修改视窗标题 -
screen.fill()------颜色填充 -
pygame.event-------用来处理事件
- pygame.event.get() -------获取事件
2.载入游戏需要的素材
background_img = pygame.image.load("./img/background.png").convert()
bullet_img = pygame.image.load("./img/bullet.png").convert()
rock_img = pygame.image.load("./img/rock.png").convert()
player_img = pygame.image.load("./img/player.jpg").convert()
pygame.mixer.init()
shoot_sound = pygame.mixer.Sound("./sound/bullet.wav")
expl_sounds1 = pygame.mixer.Sound("./sound/B-R.wav")
expl_sounds2 = pygame.mixer.Sound("./sound/R-P.wav")
expl_sounds1.set_volume(0.1)
expl_sounds2.set_volume(0.1)
pygame.mixer.music.load("./sound/background.wav")
pygame.mixer.music.play(-1)
-
pygame.image.load() -------从文件中加载图片 -
pygame.mixer.Sound() ------- 创建一个Sound对象,加载声音文件
- set_volume()-------用来调整音量
-
pygame.mixer.music.load() ------- 加载一个音乐文件 -
pygame.mixer.music.play()------播放一个音乐文件
2.定义一个玩家的类对象
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(player_img, (50, 50))
self.image.set_colorkey((255, 255, 255))
self.rect = self.image.get_rect()
self.rect.x = 200
self.rect.y = 500
self.speedx = 8
self.speedy = 8
self.radius = 22.5
self.health = 100
def update(self):
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_RIGHT]:
self.rect.x += self.speedx
elif key_pressed[pygame.K_LEFT]:
self.rect.x -= self.speedx
if self.rect.right > 500:
self.rect.right = 500
elif self.rect.left < 0:
self.rect.left = 0
-
sprite 是用在2D游戏中,快速绘图和擦除图形的一个模块 -
pygame.transform.scale()------重新设置载入图片的大小,参数包含:图像,包含图像横、纵长度的元组 -
rect 即rectangle,矩形的意思,其可以返回矩形的一些特征值,如上文代码中应用到的:
- rect.x-----左上角x坐标
- rect.y------左上角y坐标
- rect.right------矩形右边界
- rect.left------矩形左边界
- rect.buttom------矩形下边界
- rect.top------矩形上边界
- rect.centerx------矩形中心x坐标
- rect.centery------矩形中心y坐标
-
pygame.key.get_pressed()--------获取键盘输入 -
框框碰撞和圆形碰撞,很明显,圆形应该更精确一些
3.定义飞行陨石的类对象
import random
class Rock(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image_org = pygame.transform.scale(rock_img, (50, 50))
self.image_org.set_colorkey((0, 0, 0,))
self.image = self.image_org.copy()
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, 500)
self.rect.y = random.randrange(-100, -10)
self.speedx = random.randrange(-2, 2)
self.speedy = random.randrange(2, 8)
self.radius = 25
self.total_degree = 0
self.rot_degree = random.randrange(-3, 3)
def rotate(self):
self.total_degree += self.rot_degree
self.total_degree = self.total_degree % 360
self.image = pygame.transform.rotate(self.image_org, self.total_degree)
center = self.rect.center
self.rect = self.image.get_rect()
self.rect.center = center
def update(self):
self.rotate()
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.left > 500 or self.rect.right < 0 or self.rect.bottom > 600:
self.rect.x = random.randrange(0, 500)
self.rect.y = random.randrange(-100, -10)
-
pygame.transform.rotate() ------图像的旋转函数,参数为:图像,旋转角度 -
需要重点理解的是为何要拷贝一层原始图像来进行旋转
-
不拷贝原始图像的情况下:图像旋转3°,又旋转3°,双旋转3°,叒旋转3°,一直持续下去。 由于界面的刷新频率为每秒钟60次,这种旋转会造成图像的失真,就是,转着转着就不找到到哪里去了 -
拷贝原始图像的情况下:图像从0°旋转3°,图像从0°旋转6°,图像从0°旋转9°,每次图像都是从0°开始旋转,可以有效的避免失真。 这也是为什么设置total_degree的原因 -
为什么要重新定义旋转后图像的框框,并使其中心与原框框的中心重合: 如果说成是:定义旋转中心应该会好理解一些吧,如果不重新定义图像旋转的时候会产生晃动和震荡
4.定义发射子弹的类对象
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(bullet_img, (10, 50))
self.image.set_colorkey((0, 0, 0))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.bottom = y
self.speedy = -10
def update(self):
self.rect.y += self.speedy
if self.rect.y < 0:
self.kill()
- self.kill() -------清理sprite里的self类。self指代的是定义类对象
5.定义一个事物组
定义事物(sprite:如上文定义的Player、Rock、Bullet)群组,将所有事物都放入其中,共同执行操作。
all_sprites = pygame.sprite.Group() # 定义一个组
player = Player() # 创建一个对象实例,即一个玩家
all_sprites.add(player) # 将玩家对象实例加入到全体事物组中
rocks = pygame.sprite.Group() # 创建一个石头组
for i in range(5):
rock = Rock() # 创建一个对象实例,即一个石头
all_sprites.add(rock) # 将石头加入到全体事物组
rocks.add(rock) # 将石头加入到石头组
bullets = pygame.sprite.Group() # 创建一个子弹组
6.为Player定义发射子弹的执行函数
以下函数添加进Player类里,即self指代的为Player
def shoot(self): # 定义发射子弹的函数
bullet = Bullet(self.rect.centerx, self.rect.top) # 创建一个对象实例,即创建一个子弹,子弹产生于飞机模型的顶端中心处。
all_sprites.add(bullet) # 将子弹加入到全体事物组
bullets.add(bullet) # 将子弹加入到子弹组
shoot_sound.play() # 发射子弹时,播放子弹发射的声音
7.定义血条框框
def draw_health(surf, hp, x, y):
if hp < 0:
hp = 0
BAR_LENGTH = 100
BAR_HEIGHT = 10
outline_rect = pygame.Rect(x, y, BAR_LENGTH, BAR_HEIGHT)
fill = (hp / 100) * BAR_LENGTH
fill_rect = pygame.Rect(x, y, fill, BAR_HEIGHT)
pygame.draw.rect(surf, (255, 255, 255), outline_rect, 2)
pygame.draw.rect(surf, (0, 255, 0), fill_rect)
不知道各位有没有注意到:
画框框和画色块的两个函数为同一个函数,但是由于传的参数个数不同才会出现不同的效果。
如果把血条轮廓线框框的参数2去掉,就会显示为白色的填充色块,而非白色线框。
- pygame.draw.rect()-------用来画填充色块或者线框,色块三个参数,线框四个参数,第四个参数为线宽
- pygame.Rect()--------用来储存矩形对象,包含矩形的一些特征属性
8.定义得分文本框
def draw_text(surf, text, size, x, y): # 有五个参数,画在哪个表面,文本,文本字体大小,x坐标,y坐标
font = pygame.font.Font(font_name, size) # 创建一个字体对象,参数为字体的名称,文本字体大小
text_surface = font.render(text, True, (255, 255, 255)) # 在一个新表面上绘制文本,参数为:内容,抗锯齿化,字体颜色
text_rect = text_surface.get_rect() # 获得新表面的矩形框框作为得分文本框
text_rect.centerx = x # 将传入的参数x定义为得分文本框的中心的x坐标
text_rect.top = y # 将传入的参数y定义为得分文本框的上边界
surf.blit(text_surface, text_rect) # 将绘制有文本的新表面,绘制到传入的表面参数上;并取第二个参数,文本框的左上角坐标为绘制坐标
- font_name 需要在主函数中定义 font_name = pygame.font.match_font(“arial”)
- pygame.font.Font()------创建一个字体对象,参数为字体的名称,字体的大小
- pygame.font.Font().render()-------在一个新表面上绘制文本,参数为:内容,平滑度(抗锯齿化,非抗锯齿化),字体颜色,背景色
- pygame.Surface.blit() -------- 将一个图像绘制到另一个图像上方
9.主体函数填充
9.1 更新游戏进程
all_sprites.update()
hits = pygame.sprite.groupcollide(rocks, bullets, True, True)
for hit in hits:
expl_sounds1.play()
r = Rock()
all_sprites.add(r)
rocks.add(r)
sorce += int(hit.radius)
hits2 = pygame.sprite.spritecollide(player, rocks, True, pygame.sprite.collide_circle)
for hit2 in hits2:
expl_sounds2.play()
player.health -= hit2.radius
if player.health <= 0:
running = False
r = Rock()
all_sprites.add(r)
rocks.add(r)
-
sorce 变量需要在循环体之外定义 sorce=0 -
pygame.sprite.groupcollide()---------sprite组之间的碰撞检测,返回一个字典,参数为两个组,两个布尔值 -
pygame.sprite.spritecollide()----------单个sprite和一个sprite组之间的矩形碰撞检测,其检测类型可用过参数pygame.sprite.collide_circle更改为圆型检测,返回值为一个列表
9.3显示游戏画面
screen.blit(background_img, (0, 0))
all_sprites.draw(screen)
draw_text(screen, str(sorce), 18, 250, 10)
draw_health(screen, player.health, 5, 10)
pygame.display.update()
- pygame.display.update() --------更新事物界面显示
10.整合代码
import random
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(player_img, (50, 50))
self.image.set_colorkey((255, 255, 255))
self.rect = self.image.get_rect()
self.rect.x = 200
self.rect.y = 500
self.speedx = 8
self.speedy = 8
self.radius = 22.5
self.health = 100
def update(self):
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_RIGHT]:
self.rect.x += self.speedx
elif key_pressed[pygame.K_LEFT]:
self.rect.x -= self.speedx
if self.rect.right > 500:
self.rect.right = 500
elif self.rect.left < 0:
self.rect.left = 0
def shoot(self):
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
shoot_sound.play()
class Rock(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image_org = pygame.transform.scale(rock_img, (50, 50))
self.image_org.set_colorkey((0, 0, 0,))
self.image = self.image_org.copy()
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, 500)
self.rect.y = random.randrange(-100, -10)
self.speedx = random.randrange(-2, 2)
self.speedy = random.randrange(2, 8)
self.radius = 25
self.total_degree = 0
self.rot_degree = random.randrange(-3, 3)
def rotate(self):
self.total_degree += self.rot_degree
self.total_degree = self.total_degree % 360
self.image = pygame.transform.rotate(self.image_org, self.total_degree)
center = self.rect.center
self.rect = self.image.get_rect()
self.rect.center = center
def update(self):
self.rotate()
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.left > 500 or self.rect.right < 0 or self.rect.bottom > 600:
self.rect.x = random.randrange(0, 500)
self.rect.y = random.randrange(-100, -10)
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(bullet_img, (10, 50))
self.image.set_colorkey((0, 0, 0))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.bottom = y
self.speedy = -10
def update(self):
self.rect.y += self.speedy
if self.rect.y < 0:
self.kill()
def draw_health(surf, hp, x, y):
if hp < 0:
hp = 0
BAR_LENGTH = 100
BAR_HEIGHT = 10
file = (hp / 100) * BAR_LENGTH
outline_rect = pygame.Rect(x, y, BAR_LENGTH, BAR_HEIGHT)
fill_rect = pygame.Rect(x, y, file, BAR_HEIGHT)
pygame.draw.rect(surf, (0, 255, 0), fill_rect)
pygame.draw.rect(surf, (255, 255, 255), outline_rect, 2)
def draw_text(surf, text, size, x, y):
font = pygame.font.Font(font_name, size)
text_surface = font.render(text, True, (255, 255, 255))
text_rect = text_surface.get_rect()
text_rect.centerx = x
text_rect.top = y
surf.blit(text_surface, text_rect)
if __name__ == '__main__':
pygame.init()
pygame.mixer.init()
WIDTH = 500
HEIGHT = 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("太空生存")
clock = pygame.time.Clock()
background_img = pygame.image.load("./img/background.png").convert()
bullet_img = pygame.image.load("./img/bullet.png").convert()
rock_img = pygame.image.load("./img/rock.png").convert()
player_img = pygame.image.load("./img/player.jpg").convert()
shoot_sound = pygame.mixer.Sound("./sound/bullet.wav")
expl_sounds1 = pygame.mixer.Sound("./sound/B-R.wav")
expl_sounds2 = pygame.mixer.Sound("./sound/R-P.wav")
expl_sounds1.set_volume(0.1)
expl_sounds2.set_volume(0.1)
pygame.mixer.music.load("./sound/background.wav")
pygame.mixer.music.play(-1)
all_sprites = pygame.sprite.Group()
rocks = pygame.sprite.Group()
bullets = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(5):
rock = Rock()
all_sprites.add(rock)
rocks.add(rock)
sorce = 0
font_name = pygame.font.match_font("arial")
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.shoot()
all_sprites.update()
hits = pygame.sprite.groupcollide(rocks, bullets, True, True)
for hit in hits:
expl_sounds1.play()
r = Rock()
all_sprites.add(r)
rocks.add(r)
sorce += int(hit.radius)
hits2 = pygame.sprite.spritecollide(player, rocks, True, pygame.sprite.collide_circle)
for hit2 in hits2:
expl_sounds2.play()
player.health -= hit2.radius
if player.health <= 0:
running = False
r = Rock()
all_sprites.add(r)
rocks.add(r)
screen.fill((252, 112, 99))
screen.blit(background_img, (0, 0))
all_sprites.draw(screen)
draw_text(screen, str(sorce), 18, 250, 10)
draw_health(screen, player.health, 5, 10)
pygame.display.update()
pygame.quit()
expl_sounds2.play()
player.health -= hit2.radius
if player.health <= 0:
running = False
r = Rock()
all_sprites.add(r)
rocks.add(r)
screen.fill((252, 112, 99))
screen.blit(background_img, (0, 0))
all_sprites.draw(screen)
draw_text(screen, str(sorce), 18, 250, 10)
draw_health(screen, player.health, 5, 10)
pygame.display.update()
pygame.quit()
|