找回密码
 立即注册
查看: 255|回复: 0

Python:pygame游戏开发

[复制链接]
发表于 2022-7-7 09:02 | 显示全部楼层 |阅读模式
写在前面

“我有一个梦想,那就是有生之年做出一款属于自己的游戏。”不知道屏幕前的你是否曾经有和我一样的想法,总觉得市面上的游戏不完全符合你的胃口,想要自己开发出一款属于自己的独有的游戏。此时,如果你正好学了Python,那么你的机会来了,python也是可以做游戏的!本篇文章将介绍Python一个非常有趣又功能强大库,它所拥有的模块几乎包含了2D游戏常用功能的实现,让你用Python也能开发出一款小型的2d游戏,它的名字就是—— pygame。


什么是pygame?

Pygame 是一个专门用来开发游戏的 Python 模块,主要为开发、设计 2D 电子游戏而生,具有免费、开源,支持多种操作系统,具有良好的跨平台性等优点。它提供了诸多操作模块,比如图像模块(image)、声音模块(mixer)、输入/输出(鼠标、键盘、显示屏)模块等。简单来说,如果你使用pygame,理论上可以开发设计市面上所有的2d类型游戏(仅仅是理论上)。
但是,实际上,我学习pygame并不是说要用它来开发一款完美的游戏,pygame太过于底层,许多功能都要自己来封装实现。也许你用pygame开发一款超级玛丽时间,别人都能学会C#用unity开发和平精英了(夸张)。那么既然如此,为什么还要来学它?


学习pygame的用处

Pygame适合小型2d游戏开发,比如于飞机大战、贪吃蛇、扫雷等,游戏越大,开发越困难。所以,学习pygame你可以复刻一款小时候玩的经典游戏,比如超级玛丽,魂斗罗之类的,然后更改一些游戏角色及内容,哄哄家里的弟弟妹妹(就是装X呗)。


更重要的一点,我希望你能够通过学习pygame深入理解python面向对象的思想,python面向对象是这门语言的精髓之一。掌握python面向对象的思想,也是你成功跨过python基础的标志之一,对于你后面选择的学习方向(web开发,自动化,爬虫等等)的入门非常有帮助。有了python面向对象的牢固基础,学习python高级的东西你会觉得也就那么回事,有一法破万法的效果。
当然,值得一提的是如果你真的对游戏开发感兴趣,又有python基础,那么通过pygame你可以初步了解关于游戏开发的一些基础知识,等到用pygame开发一两个小游戏,再来思考一下自己适不适合游戏开发,毕竟游戏开发的精髓都是相通的,那些高级游戏引擎只是把一些基础功能封装好了让你用,pygame只是需要你用自己的手去打造自己的工具箱。
Pygame里的游戏世界

在讲解具体代码之前,还是要科普一些pygame里游戏的基础知识。
你印象中的2d游戏由哪些成分构成?首先要有一个窗口,它规定游戏画面占你的屏幕的尺寸大小,也可以设计游戏名字,背景等等。其次就是各种场景,一个游戏可以看做是由许多个场景组成的,比如超级玛丽的开始菜单场景,第一关,第二关等等。场景要有内容,比如背景,主角,敌人,子弹,物品(如超级玛丽的砖块等等)。在后面就是主角与敌人或者其它对象的各种交互动作,如白色玛丽会发射子弹,踩到敌人压扁对方等等。最后则是贯穿场景的各种音效,声音等等。
Pygame简单讲解

本篇文章不打算太深入pygame,旨在增加你对python的理解,以及吸引你对pygame游戏开发的一点点兴趣,或者你之前学过pygame但忘记了,希望本篇文章可以帮到你。如果你想要系统学习pygame,建议先看B站免费教程,然后自己通过官网深入学习pygame。
废话不多说,我将通过自己初学时学的一个例子来让你实现pygame的初步入门。
安装pygame

pip install pygame 一直都是通过pycharm安装python第三方库,简直不要太爽。
游戏最小系统

什么是游戏最小系统(不知道有没有这个名词)?可以理解为游戏能够运行不报错的最小代码。
# 导入库
import pygame

# 初始化操作,游戏开始之前,运行该代码进行初始化
pygame.init()

# 创建游戏窗口,注意设置了window这个变量
window = pygame.display.set_mode((600,400))

# 设置游戏标题
pygame.display.set_caption('游戏最小系统')

# 给窗口设置背景颜色
window.fill((255,255,255))

运行上面代码你会发现左上角出现一个窗口,然后一闪即过,这是正常的。这里就要思考python代码的运行,通过执行上述代码,你创建了一个黑色窗口,但是当代码执行完会发生什么,当然是python自动退出了。所以你要使用一定的方法不让代码结束运行,这就要添加“死循环”了,这很好理解,while true 会一遍又一遍的运行里面的代码而不退出。
# 保持游戏运行状态(游戏循环)
while True:

    # 检测事件发生,先把事件全部拿过来,再看发生的是哪一件事。
    for event in pygame.event.get():
        # 检测关闭按钮被点击的事件
        if event.type == pygame.QUIT:
            # 如果按下屏幕里的X号就会退出
            exit()看到循环里面添加的for循环及if语句吗?它们有什么作用。由于循环会一直执行,里面的代码会一遍又一遍进行检测,所以在里面就可以有很多操作了。这就是之前提到的主角与敌人或其它物品的交互部分,代码会一直检测发生了什么事情,并作出反应,也包括开发游戏常说的碰撞检测。
所以结合上面,最小系统就出来了,它只是一个黑屏,但是它是游戏的基础框架。
import pygame
# 初始化操作
pygame.init()

# 创建游戏窗口
window = pygame.display.set_mode((600,400))

# 设置游戏标题
pygame.display.set_caption('游戏最小系统')

# 设置背景颜色
window.fill((255,255,255))

# ========游戏开始页面静态效果==========

# 保持游戏运行状态(游戏循环)
while True:
    # ===========游戏帧的刷新===========
    # 检测事件
    for event in pygame.event.get():
        # 检测关闭按钮被点击的事件
        if event.type == pygame.QUIT:
            # 退出
            exit()
        


python飞机大战

下面用一个简单的飞机大战游戏,串一下pygame基础知识。
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()  : 获取音量
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)3.事件(键盘事件,鼠标事件)
键盘事件,这些会涉及到的日程安排操作,比如游戏批量的上下左右,或者人物中的前进、后继等操作,都需要键盘来配合执行。一个事件的关键点,该事件可以进行连续的连续性控制。一个关键点、组合属性等以连续性的方式提供一系列事件,一系列常用的属性将通过一系列的连续性事件进行排序。

  • K_BACKSPACE:退格键(Backspace)
  • K_TAB:制表键(Tab)
  • K_CLEAR:清除键
  • K_RETURN:回车键(Enter)
  • 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:鼠标键按下事件
4.碰撞检测

  • pygame.sprite.collide_rect():精灵之间的矩形检测,即两个矩形区域是否有交汇,返回一个布尔值。
  • pygame.sprite.collide_circle():两个精灵之间的圆形检测,即圆形区域是否有交汇,返回一个布尔值。
  • pygame.sprite.collide_mask():两个精灵之间的像素蒙版检测,更为精准的一种检测方式。
  • pygame.sprite.spritecollide():精灵和精灵组之间的矩形碰撞检测,一个组内的所有精灵会逐一地对另外一个单个精灵进行碰撞检测,返回值是一个列表,包含了发生碰撞的所有精灵。
  • pygame.sprite.spritecollideany():精灵和精灵组之间的矩形碰撞检测,上述函数的变体,当发生碰撞时,返回组内的一个精灵,无碰撞发生时,返回 None。
  • pygame.sprite.groupcollide():检测在两个组之间发生碰撞的所有精灵,它返回值是一个字典,将第一组中发生碰撞的精灵作为键,第二个组中发生碰撞的精灵作为值。
5.更新
pygame.display.update()
pygame.display.flip()flip函数将重新绘制整个屏幕对应的窗口。update函数仅仅重新绘制窗口中有变化的区域。如果仅仅是几个物体在移动,那么他只重绘其中移动的部分,没有变化的部分,并不进行重绘。update比flip速度更快。因此在一般的游戏中,如果不是场景变化非常频繁的时候,建议使用update函数,而不是flip函数。
总结及源码

pygame可用函数有很多,但是真的不难,用文字讲述很麻烦,所以我只是列举了一些常用函数与方法,当你用到的时候记得去官网或者百度搜搜就可以了,把完整代码附在下面,仅供参考。

python粗糙版飞机大战
https://www.zhihu.com/video/1523415814270734336
# 导入两个库
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相关教程,需要一些源码练手,我将在下一篇整理几个常见的小游戏源码,供你学习参考。
想要获取飞机大战源码及素材的童鞋请关注微信订阅号“Python编程札记”,后台回复“005”还有更多有趣内容等着你!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-25 23:03 , Processed in 0.093651 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表