赞
踩
太空生存小游戏
pygame官方教程链接
import pygame pygame.init() # 初始化 WIDTH = 500 HEIGHT = 600 screen = pygame.display.set_mode((WIDTH, HEIGHT)) # 创建视窗,视窗参数为一个元组,第0个元素表示宽度,第1个元素表示高度 pygame.display.set_caption("太空生存") # 修改视窗标题 clock = pygame.time.Clock() # (创建界面刷新频率?即帧率?大概是可以这么理解的) running = True # 定义游戏的运行状态 while running: # 游戏开始运行 clock.tick(60) # 界面一秒钟刷新60次 # 获取用户输入信息 for event in pygame.event.get(): # 获取用户的输入列表,遍历列表 if event.type == pygame.QUIT: # 点击视窗的关闭按钮,则关闭视窗 running = False # 退出循环 # 根据用户输入信息更新游戏状态 # 显示视窗内容 screen.fill((0, 0, 0)) # 界面填充颜色为黑色,RGB值 pygame.quit() # 结束游戏
运行结果:
pygame.init()-------初始化pygame模块
pygame.quit()-------退出pygame模块
pygame.display.set_mode() -------创建视窗
pygame.display.set_caption()-------修改视窗标题
screen.fill()------颜色填充
pygame.event-------用来处理事件
- pygame.event.get() -------获取事件
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) # 循环播放背景音,函数中的参数表示播放的次数,-1表示循环播放无限次
pygame.image.load() -------从文件中加载图片
pygame.mixer.Sound() ------- 创建一个Sound对象,加载声音文件
- set_volume()-------用来调整音量
pygame.mixer.music.load() ------- 加载一个音乐文件
pygame.mixer.music.play()------播放一个音乐文件
class Player(pygame.sprite.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 # 设置框框左上顶角在视窗中的x轴坐标值 self.rect.y = 500 # 设置框框左上顶角在视窗中的y轴坐标值 self.speedx = 8 # 设定沿x运动速度 self.speedy = 8 # 设定沿y运动速度 self.radius = 22.5 # 根据二维物体图像的大小来设置半径,用来进行更精确地对象碰撞检测。框框和框框之间的碰撞,明显不如圆和圆之间的碰撞更为精确 self.health = 100 # 定义玩家的生命值 def update(self): # 定义执行动作 key_pressed = pygame.key.get_pressed() # 获取键盘输入,是一个列表,包含每个按键的状态,按压为True,松开为False if key_pressed[pygame.K_RIGHT]: # 如果输入的是 → self.rect.x += self.speedx # x坐标,以设定速度增加,即向右运动 elif key_pressed[pygame.K_LEFT]: # 如果输入的是 ← self.rect.x -= self.speedx # x坐标,以设定速度减小,即向左运动 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()--------获取键盘输入
框框碰撞和圆形碰撞,很明显,圆形应该更精确一些
import random # 实现飞行陨石的随机掉落 class Rock(pygame.sprite.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) # 设置框框左上顶角在视窗中的x轴坐标值,让陨石在x坐标为0,500的范围内掉落 self.rect.y = random.randrange(-100, -10) # 设置框框左上顶角在视窗中的y轴坐标值,让陨石在x坐标为-100,-10的范围内掉落,视窗外不可见 self.speedx = random.randrange(-2, 2) # 设定x方向运动速度 self.speedy = random.randrange(2, 8) # 设定y方向运动速度 self.radius = 25 # 根据二维物体图像的大小来设置半径,用来进行更精确地对象碰撞检测。 self.total_degree = 0 # 设置陨石旋转的角度 self.rot_degree = random.randrange(-3, 3) # 设置陨石旋转角度变化的速度即角速度 # 上述实现结果就是:陨石一边旋转,一边移动 def rotate(self): # 定义陨石的旋转运动 self.total_degree += self.rot_degree # 每执行一次rotate(),旋转角度就发生一次变化,0-3-6-9.... self.total_degree = self.total_degree % 360 # 当旋转角度大于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 # 定义x方向运动 self.rect.y += self.speedy # 定义y方向运动 if self.rect.left > 500 or self.rect.right < 0 or self.rect.bottom > 600: # 当石头的框框移动到视窗之外后,重新定义石头的坐标 self.rect.x = random.randrange(0, 500) # 定位石头的x坐标 self.rect.y = random.randrange(-100, -10) # 定位石头的y坐标
pygame.transform.rotate() ------图像的旋转函数,参数为:图像,旋转角度
需要重点理解的是为何要拷贝一层原始图像来进行旋转
不拷贝原始图像的情况下:图像旋转3°,又旋转3°,双旋转3°,叒旋转3°,一直持续下去。
由于界面的刷新频率为每秒钟60次,这种旋转会造成图像的失真,就是,转着转着就不找到到哪里去了
拷贝原始图像的情况下:图像从0°旋转3°,图像从0°旋转6°,图像从0°旋转9°,每次图像都是从0°开始旋转,可以有效的避免失真。
这也是为什么设置total_degree的原因
为什么要重新定义旋转后图像的框框,并使其中心与原框框的中心重合:
如果说成是:定义旋转中心应该会好理解一些吧,如果不重新定义图像旋转的时候会产生晃动和震荡
class Bullet(pygame.sprite.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 # 设置框框左上顶角在视窗中的x轴坐标值,即传入的参数x
self.rect.bottom = y # 设置框框左上顶角在视窗中的y轴坐标值,即传入的参数y
self.speedy = -10 # 设定y运动速度,子弹只会沿y轴运动
def update(self): # 定义执行动作
self.rect.y += self.speedy # 子弹的运动
if self.rect.y < 0: # 当子弹运动到视窗外的时候,删除子弹
self.kill()
- self.kill() -------清理sprite里的self类。self指代的是定义类对象
定义事物(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() # 创建一个子弹组
为何要单独创建一个石头组和一个子弹组?
之后进行石头和子弹的碰撞检查的时候需要使用到这两个组。
以下函数添加进Player类里,即self指代的为Player
def shoot(self): # 定义发射子弹的函数
bullet = Bullet(self.rect.centerx, self.rect.top) # 创建一个对象实例,即创建一个子弹,子弹产生于飞机模型的顶端中心处。
all_sprites.add(bullet) # 将子弹加入到全体事物组
bullets.add(bullet) # 将子弹加入到子弹组
shoot_sound.play() # 发射子弹时,播放子弹发射的声音
def draw_health(surf, hp, x, y): # 定义画血条框框的函数,一共需要四个参数,画在哪个表面,血量,x坐标,y坐标
if hp < 0: # 如果一开始血量就少于0,就定义血量为0,毕竟没有负的血量
hp = 0
BAR_LENGTH = 100 # 定义血条的长度
BAR_HEIGHT = 10 # 定义血条的高度
outline_rect = pygame.Rect(x, y, BAR_LENGTH, BAR_HEIGHT) # 定义血条轮廓线框框,(x,y)为框框左上角坐标,框框的长度,高度
fill = (hp / 100) * BAR_LENGTH # 定义填充框框的色块的长度,与当前的血量有关。
fill_rect = pygame.Rect(x, y, fill, BAR_HEIGHT) # 定义填充填充的色块,(x,y)为框框左上角坐标,表示框框左边开始填充
pygame.draw.rect(surf, (255, 255, 255), outline_rect, 2) # 在传入的sufr表面,画血条轮廓线框框,为白色,两个像素厚度
pygame.draw.rect(surf, (0, 255, 0), fill_rect) # 在传入的sufr表面,画填充色块,为绿色
不知道各位有没有注意到:
画框框和画色块的两个函数为同一个函数,但是由于传的参数个数不同才会出现不同的效果。
如果把血条轮廓线框框的参数2去掉,就会显示为白色的填充色块,而非白色线框。
- pygame.draw.rect()-------用来画填充色块或者线框,色块三个参数,线框四个参数,第四个参数为线宽
- pygame.Rect()--------用来储存矩形对象,包含矩形的一些特征属性
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() -------- 将一个图像绘制到另一个图像上方
all_sprites.update() # 全体事物组中的事物都执行update()方法,Player可以移动,Rock开始随机掉落,Bullet可以移动(前提是已经产生了) hits = pygame.sprite.groupcollide(rocks, bullets, True, True) # 检查石头组和子弹组二者之间的碰撞,并决定碰撞后是否消失,返回结果是一个字典 for hit in hits: # hits中 rock为key,bullet为value 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) # 检查飞机和石头的碰撞,碰撞后石头消失,检查方式是圆形碰撞,返回结果为一个列表,列表中元素为与player发生碰撞的rock for hit2 in hits2: # 遍历字典 expl_sounds2.play() # 若字典有值,即代表发生了碰撞,播放碰撞爆炸声 player.health -= hit2.radius # 玩家血量减少,减少值为rock的半径 if player.health <= 0: # 当血量小于等于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更改为圆型检测,返回值为一个列表
screen.blit(background_img, (0, 0)) # 在screen表面上,以(0,0)为起始点,绘制图像,即加载背景图像
all_sprites.draw(screen) # 将所有的事物显示到screen表面上
draw_text(screen, str(sorce), 18, 250, 10) # 调用得分文本框函数,绘制得分文本框
draw_health(screen, player.health, 5, 10) # 调用血条框框函数,绘制血条框框
pygame.display.update() # 进行画面更新
- pygame.display.update() --------更新事物界面显示
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()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。