一、python飞机大战
下面用一个简单的飞机大战游戏,串一下pygame基础知识。
1.1 音乐
这里列举一些常用函数,详细内容请到pygame官网查看。
- pygame.mixer.music.load() :加载一个音乐文件用于播放;
- pygame.mixer.music.play() :播放音乐;
- pygame.mixer.music.rewind() :重新播放音乐;
- pygame.mixer.music.stop() : 结束音乐播放;
- pygame.mixer.music.pause() :暂停音乐播放;
- pygame.mixer.music.unpause() :恢复音乐播放;
- pygame.mixer.music.set_volume() :设置音量;
- pygame.mixer.music.get_volume() : 获取音量。
1.2 精灵(spirte)
pygame场景里的动态物体都可视为精灵,如主角,敌人,子弹,可移动背景等等,换句话说,精灵就是一些动态图片,你要对这些图片进行一些交互操作,如移动,碰撞,爆炸等等。
Pygame提供了一个处理精灵的模块,也就是sprite(pygame.sprite)模块。我们使用该类Sprite来创建一个子类,真正达到处理精灵的目的,该子类提供了操作精灵的常用属性和方法,如下所示:
- self.image:加载要显示的精灵,控制图片大小和填充颜色;
- self.rect:精灵图片显示在哪个位置;
- Sprite.update():刷新图,相应效果生效;
- Sprite.add():添加精灵图到精灵组中(groups);
- Sprite.remove():从精灵组中删除删除的精灵图;
- Sprite.kill():删除精灵组中所有的精灵;
- Sprite.alive():判断某个精灵是否属于精灵组。
当游戏中有大量的精灵时,操作它们将变得复杂,此时通过构建精灵容器(group 类)也就是精灵组来统一管理这些精灵。构建方法如下:
# 创建精灵组 group = pygame.sprite.Group() # 向组内添加一个精灵 group.add(sprite1)
1.3 事件(键盘事件,鼠标事件)
键盘事件,这些会涉及到的日程安排操作,比如游戏批量的上下左右,或者人物中的前进、后继等操作,都需要键盘来配合执行。
一个事件的关键点,该事件可以进行连续的连续性控制。一个关键点、组合属性等以连续性的方式提供一系列事件,一系列常用的属性将通过一系列的连续性事件进行排序。
- K_BACKSPACE:退格键(Backspace);
- K_TAB:制表键(Tab);
- K_CLEAR:清除键;
- K_RETURN:回车键(Enter);
- K_PAUSE:暂停键(Pause);
- K_PAUSE:暂停键(Pause);
- K_ESCAPE:退出键(Escape);
- K_SPACE:空格键(空格);
- K_DELETE:删除键(delete);
- K_UP向上:箭头(向上箭头);
- K_DOWN:箭头(向下箭头);
- K_RIGHT:向左(右箭头);
- K_LEFT:向左箭头(左箭头)。
- KMOD_ALT:同时按下Alt键;
鼠标事件,Pygame 提供了三个鼠标事件:
- pygame.event.MOUSEMOTION:鼠标移动事件;
- pygame.event.MOUSEBUTTONUP:鼠标键释放事件;
- pygame.event.MOUSEBUTTONDOWN:鼠标键按下事件。
1.4 碰撞检测
- pygame.sprite.collide_rect():精灵之间的矩形检测,即两个矩形区域是否有交汇,返回一个布尔值;
- pygame.sprite.collide_circle():两个精灵之间的圆形检测,即圆形区域是否有交汇,返回一个布尔值;'
- pygame.sprite.collide_mask():两个精灵之间的像素蒙版检测,更为精准的一种检测方式;
- pygame.sprite.spritecollide():精灵和精灵组之间的矩形碰撞检测,一个组内的所有精灵会逐一地对另外一个单个精灵进行碰撞检测,返回值是一个列表,包含了发生碰撞的所有精灵;
- pygame.sprite.spritecollideany():精灵和精灵组之间的矩形碰撞检测,上述函数的变体,当发生碰撞时,返回组内的一个精灵,无碰撞发生时,返回 None;
- pygame.sprite.groupcollide():检测在两个组之间发生碰撞的所有精灵,它返回值是一个字典,将第一组中发生碰撞的精灵作为键,第二个组中发生碰撞的精灵作为值。
1.5 更新
- pygame.display.update()
- pygame.display.flip()
flip函数将重新绘制整个屏幕对应的窗口。update函数仅仅重新绘制窗口中有变化的区域。如果仅仅是几个物体在移动,那么他只重绘其中移动的部分,没有变化的部分,并不进行重绘。
update比flip速度更快。因此在一般的游戏中,如果不是场景变化非常频繁的时候,建议使用update函数,而不是flip函数。
1.6 总结及源码
pygame可用函数有很多,但是真的不难,用文字讲述很麻烦,所以我只是列举了一些常用函数与方法,当你用到的时候记得去官网或者百度搜搜就可以了,把完整代码附在下面,仅供参考:
# 导入两个库 import pygame import random # 常量,屏幕宽高 WIDTH, HEIGHT = 800, 600 # 初始化操作 pygame.init() pygame.mixer.init() # 创建游戏窗口 screen = pygame.display.set_mode((WIDTH, HEIGHT)) # 设置游戏标题 pygame.display.set_caption('飞机大战') # 添加音乐 pygame.mixer.music.load('./sound/bgLoop.wav') pygame.mixer.music.set_volume(0.5) # 音量 pygame.mixer.music.play(-1, 0) # 添加系统时钟,用于设置帧的刷新 FPS = 40 clock = pygame.time.Clock() # 创建用户自定义事件,每隔2000毫秒触发一次事件,随机创建敌人 CREATE_ENEMY = pygame.USEREVENT # 每隔2000毫秒,会传递一个信号 pygame.time.set_timer(CREATE_ENEMY, 2000) # 对于精灵定义了主角,子弹,敌人,爆炸,可移动背景四个。 #class Hero(pygame.sprite.Sprite) #class Bullet(pygame.sprite.Sprite) #class Enemy(pygame.sprite.Sprite) #class Explode(pygame.sprite.Sprite) #class BackGround(pygame.sprite.Sprite) # 主角 class Hero(pygame.sprite.Sprite): def __init__(self, speed): super().__init__() # 调用父类的初始化方法 self.image = pygame.image.load('./image/plane.png') self.rect = self.image.get_rect() # 对图片进行一些尺寸处理 self.rect.width *= 0.5 self.rect.height *= 0.5 self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height)) # 主角初始化位置 self.rect.x, self.rect.y = 0, 100 self.speed = speed self.ready_to_fire = 0 def update(self, *args): keys = pygame.key.get_pressed() if keys[pygame.K_UP]: self.rect.y -= self.speed if keys[pygame.K_DOWN]: self.rect.y += self.speed if keys[pygame.K_LEFT]: self.rect.x -= self.speed if keys[pygame.K_RIGHT]: self.rect.x += self.speed if keys[pygame.K_SPACE]: if self.ready_to_fire == 0: self.fire() self.ready_to_fire += 1 if self.ready_to_fire > 5: self.ready_to_fire = 0 else: self.ready_to_fire = 0 if self.rect.x < 0: self.rect.x = 0 if self.rect.y < 0: self.rect.y = 0 if self.rect.y > HEIGHT - self.rect.height: self.rect.y = HEIGHT - self.rect.height # 子弹发射 def fire(self): bullet = Bullet(10) bullet.rect.x = self.rect.right bullet.rect.centery = self.rect.centery bullet_sprite.add(bullet) # 音效 sound = pygame.mixer.Sound('./sound/laser.wav') sound.play() class Bullet(pygame.sprite.Sprite): def __init__(self, speed): super().__init__() self.image = pygame.image.load('./image/bullet.png') self.rect = self.image.get_rect() self.speed = speed def update(self, *args): self.rect.x += self.speed if self.rect.x > WIDTH: self.kill() class Enemy(pygame.sprite.Sprite): def __init__(self, speed): super().__init__() self.image = pygame.image.load('./image/enemy1.png') self.rect = self.image.get_rect() self.rect.x = 800 self.rect.y = random.randint(0, HEIGHT) self.speed = speed def update(self, *args): self.rect.x -= self.speed if self.rect.right < 0: self.kill() class Explode(pygame.sprite.Sprite): def __init__(self): super().__init__() self.images = [pygame.image.load('./image/explode' + str(i) + '.png') for i in range(1, 4)] self.image_index = 0 self.image = self.images[self.image_index] self.rect = self.image.get_rect() self.readt_to_change = 0 sound = pygame.mixer.Sound('./sound/enemyExplode.wav') sound.play() def update(self, *args): if self.image_index < 2: self.readt_to_change += 1 if self.readt_to_change % 4 == 0: self.image_index += 1 self.image = self.images[self.image_index] else: self.kill() class BackGround(pygame.sprite.Sprite): def __init__(self): super().__init__() self.image = pygame.image.load('./image/background.jpg') self.rect = self.image.get_rect() self.ready_to_move = 0 def update(self, *args): self.rect.x -= 3 if self.rect.right <= 0: self.rect.x = self.rect.width # 初始化精灵组 bg_sprite = pygame.sprite.Group() hero_sprite = pygame.sprite.Group() enemy_sprite = pygame.sprite.Group() bullet_sprite = pygame.sprite.Group() explode_sprite = pygame.sprite.Group() # 定义人物 hero1 = Hero(4) hero_sprite.add(hero1) enemy1 = Enemy(5) enemy2 = Enemy(7) bg1 = BackGround() bg2 = BackGround() bg2.rect.x = bg2.rect.width bg_sprite.add(bg1, bg2) # 保持游戏运行状态(游戏循环) while True: # ===========游戏帧的刷新=========== clock.tick(FPS) # 检测事件 for event in pygame.event.get(): # 检测关闭按钮被点击的事件 if event.type == pygame.QUIT: # 退出 pygame.quit() exit() if event.type == CREATE_ENEMY: enemy_sprite.add(Enemy(random.randint(1, 7))) # 碰撞检测,返回字典,得到二者信息 collision = pygame.sprite.groupcollide(enemy_sprite, bullet_sprite, True, True) for enemy in collision.keys(): explode = Explode() explode.rect = enemy.rect explode_sprite.add(explode) # screen.fill((0,0,0)) for group in [bg_sprite, hero_sprite, enemy_sprite, bullet_sprite, explode_sprite]: group.update() group.draw(screen) pygame.display.update()
二、写在最后
写了很多,但是很水,我自己都不想读下去,给自己找的理由是,它就像字典一样,用到的时候查查就行了。
重点不是这个,重点是理解其中面向对象的用法,如果看不懂源码,还请不要贪玩,打好基础再学习,因为这一个飞机大战源码算是最简单(也最粗糙)的源码了。
如果你已经看过一些pygame相关教程,需要一些源码练手,我将在下一篇整理几个常见的小游戏源码,供你学习参考。