赞
踩
二娃再过一年就该上一年级了,但现阶段的拼音咋都学不进去。买了拼音挂图贴在墙上,拉都拉不到旁边。突发奇想,何不用python的pygame做个小游戏?在玩中也能学习,让学变得有趣!这对搞编程的来说小菜一碟,于是说干就干,两个晚上就成型啦,这里总结分享给有需要的小伙伴。
这不只是个游戏,更是一部科普小电影,带你体验一把星际穿越。认识星系、银河、星座、黑洞。满天繁星,璀璨的星空,总能给人带来无尽的想象和激发探索的欲望,激起求知的本能。
pygame小游戏之飞机拼音大作战,是一个专为孩子设计的游戏,旨在帮助他们学习拼音字母并激发他们对编程的兴趣,对太空的好奇心。这款游戏既有趣又教育,通过简单的Python小游戏制作过程,让孩子们能够见识游戏的开发,从中学到编程的基础知识。
兴趣是最好的老师。
游戏的背景以宇宙和航空为主题,为孩子们提供了一个奇妙的探索空间。他们可以扮演一位宇航员,驾驶飞机在太空中飞行,同时学习拼音字母。后续计划在游戏中,让孩子们需要根据屏幕上出现的字母或汉字,用正确的拼音字母来击败敌人或通过障碍物。通过这种互动的方式,他们将在玩乐中学习,增长见识,并培养对宇宙未来的探索和想象力,增加对未来的好奇心,探索欲和求知欲。
这款游戏的目的是让孩子们在享受游戏的乐趣的同时,提高他们的拼音字母识别能力和记忆能力。通过与游戏互动,他们将逐渐熟悉拼音字母的发音和书写,为他们日后的语言学习打下坚实的基础。此外,通过参与游戏的制作过程,孩子们还能培养逻辑思维、解决问题的能力和创造力,同时感受编程的乐趣。
其实很早我就曾想,既然孩子们都这么爱玩游戏,为何市面上的游戏大都让上瘾为主,毫无意义的打怪升级?为何不加上以玩儿中学习,科普知识,探索未知,增加好奇心,同步成长为主题搞一些游戏呢?
pygame小游戏飞机之拼音大作战围绕在玩中学,科普知识,激发求知欲这一理念,为孩子们提供了一个有趣而富有教育意义的学习实践,增加他们对拼音字母学习的兴趣,并为他们的未来学习之路打下坚实的基础。让我们一起探索宇宙,学习知识,开启编程的乐趣吧!
喜欢的欢迎点赞收藏,可以做为送娃的礼物。
源码包含全部素材可运行。
下载链接:https://download.csdn.net/download/qq8864/88314308
先来看下效果图:
还记得那部经典电影《星际穿越》吗?这个小游戏带你也体验一把星际穿越,也见识见识黑洞!
仰望星空,总能给人无限的遐想。
玩法很简单,把原来做的飞机大战游戏从新改版成打击拼音字母,并计算得分。每打中一个字母,就对应发出它的读音。按上下左右几个键,可以控制飞机的上下左右移动。按空格键则可以发送子弹。子弹击中拼音字母,则发出对应的字母的发音。其中还有移动的陨石是不能打的,打到则扣分。游戏的背景各不相同,从网络精选了美妙的宇宙世界,地球、月球、陨石、火星和其他各大行星,银河、星座、黑洞,璀璨的星空。
后续计划在游戏中,让孩子们需要根据屏幕上出现的字母或汉字,用正确的拼音字母来击败敌人或通过障碍物。通过这种互动的方式,他们将在玩乐中学习,增长见识,并培养对宇宙未来的探索和想象力。
设置一系列的关卡,每个的背景图都不一样,让孩子同时领略下神秘的宇宙太空。先从地球起飞,掠过月球,认识八大行星,穿越太阳系,躲过陨石,最后飞向宇宙星空,探索璀璨宇宙。
飞掠地球,下面像是蓝色的海洋,高空中俯视地球有点儿恐惧呢。
飞向月球,月球原来长这样:
飞掠火星:
火星长啥样?从火星表面经过, 近距离看看。
旁边咋还有一人造卫星,可不能把咱的卫星干下来。
飞掠八大行星:
八大行星有哪些?记得住吗?依次飞过它就印象深刻了吧。
黑洞长什么样?可能是这样吧:
后面还有飞出银河系,人马星座,到神秘的黑洞去看看。 相当于星际穿越,神秘的宇宙结合飞行动画,相当震撼!
1.下载安装python3
Python 官网:Welcome to Python.org
2.安装依赖的模块: pygame,pygame-pgu
python加入环境变量
3.替换pip安装的资源镜像(否则下载模块很慢)
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
4.图片和语音素材
5.讯飞离线语音合成工具
6.goldwave语音制作裁剪工具
源码实现很简单,有点儿基础的都能很容易看懂。这里就不做过多介绍了,基本都是pygame的一些基础用法。挑战失败弹出对话框,使用了pygame-pgu模块。
弹出对话框的简单示例:
- #退出对话框
- class QuitDialog(gui.Dialog):
- def __init__(this,main):
- title = gui.Label("Dialog Box")
- gui.Dialog.__init__(this, title, main)
- def close():
- pygame.quit()
- exit()
-
-
- app = gui.App() #初始化gui
- con = gui.Container() #生成gui的容器
- quitDlg = QuitDialog(con) #生成弹窗abc
- btn = gui.Button("game over") #生成文字为a的按钮
- btn.connect(gui.CLICK, quitDlg.close, None)#将按钮与弹窗的弹出绑定
- label = gui.Label("Are you sure you want to quit?")
- quitDlg.add(label, 2, 2)
- quitDlg.add(btn,2,3) #将按钮安放在容器(0,0)位置
- app.init(con)
这里需要注意的是pgu这个库的使用,以上代码是有问题的,网上竟然一点有用的资料也没有。会收到报错:
- Traceback (most recent call last):
- File "d:\test3\plangame\Plane.py", line 243, in <module>
- app.event(event)
- File "D:\Python\Python310\lib\site-packages\pgu\gui\app.py", line 177, in event
- container.Container.event(self,sub)
- File "D:\Python\Python310\lib\site-packages\pgu\gui\container.py", line 233, in event
- used = w._event(sub)
- File "D:\Python\Python310\lib\site-packages\pgu\gui\widget.py", line 323, in _event
- return self.event(e)
- File "D:\Python\Python310\lib\site-packages\pgu\gui\theme.py", line 354, in theme_event
- return func(sub)
- File "D:\Python\Python310\lib\site-packages\pgu\gui\container.py", line 233, in event
- used = w._event(sub)
- File "D:\Python\Python310\lib\site-packages\pgu\gui\widget.py", line 322, in _event
- self.send(e.type,e)
- File "D:\Python\Python310\lib\site-packages\pgu\gui\widget.py", line 306, in send
- names.pop(0)
- IndexError: pop from empty list
自己摸索出来的关于pgu弹出对话框的正确做法如下:
- #退出对话框
- class QuitDialog(gui.Dialog):
- def __init__(this,main):
- title = gui.Label("Dialog Box")
- gui.Dialog.__init__(this, title, main)
- #注意这里的close1,被button绑定了,除了self,还必须得有个参数
- def close1(self,w):
- print("close")
- pygame.quit()
- exit()
-
- app = gui.App() #初始化gui
- con = gui.Container() #生成gui的容器
- quitDlg = QuitDialog(con) #生成弹窗abc
- btn = gui.Button("game over") #生成文字为a的按钮
- btn.connect(gui.CLICK, quitDlg.close1, None)#将按钮与弹窗的弹出绑定
- label = gui.Label("Are you sure you want to quit?")
- quitDlg.add(label, 2, 2)
- quitDlg.add(btn,2,3) #将按钮安放在容器(0,0)位置
- app.init(con)
注意def close1这个函数是需要两个参数的,一个不行。
显示得分,字母和汉字显示的实现:
- # 加载字体文件
- font_path = "./font/SIMYOU.ttf" # 替换为你的字体文件路径
- font_size = 24
- font = pygame.font.Font(font_path, font_size)
-
- score = 0
- score_txt = "得分:"+str(score) # 要显示的文本
- text_surface = font.render(score_txt, True, (255, 255, 255))
-
-
- #......
-
- score_txt = "得分:"+str(score)
- text_surface = font.render(score_txt, True, (255, 0, 255))
- screen.blit(text_surface, (680, 10))
-
- if show_image:
- screen.blit(fail_image, (300, 100)) # 绘制图像
-
- app.paint()
- pygame.display.update()
动画的原理:其实动画就是用一张张静态的图片按顺序展示,如果顺序展示的速度比较快,人脑就会人物这是连续的。该小游戏中的动画如子弹打中后的爆破效果,就是个动画实现。其实就是提前加载好三幅图片,然后按照一定时间间隔,让精灵类的update方法自己来更新图片。
实现如下:
- 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()
在 Pygame 中,精灵(Sprites)是游戏中各种角色、物体和效果的基本元素。精灵可以包含图像、位置、速度和其他属性。使用 Pygame 中的精灵类可以有效地管理和更新多个精灵对象。
可以通过继承 Pygame 中的 pygame.sprite.Sprite 类来定义自己的精灵类。在定义精灵类时,通常需要在 __init__ 方法中设置精灵的初始位置、图像和其他属性。例如,以下代码定义了一个简单的精灵类 MySprite:
- import pygame
-
- class MySprite(pygame.sprite.Sprite):
- def __init__(self, x, y):
- super().__init__()
- self.image = pygame.Surface((50, 50))
- self.image.fill((255, 0, 0))
- self.rect = self.image.get_rect()
- self.rect.x = x
- self.rect.y = y
处理精灵对象和事件
在游戏中,通常需要处理精灵对象的交互和事件。例如,检测精灵之间的碰撞、移动精灵、响应键盘和鼠标事件等。要处理精灵对象和事件,可以使用以下代码:
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- pygame.quit()
- sys.exit()
-
- # 处理键盘事件
- keys = pygame.key.get_pressed()
- if keys[pygame.K_LEFT]:
- my_sprite.rect.x -= 5
- if keys[pygame.K_RIGHT]:
- my_sprite.rect.x += 5
- if keys[pygame.K_UP]:
- my_sprite.rect.y -= 5
- if keys[pygame.K_DOWN]:
- my_sprite.rect.y += 5
-
- # 处理碰撞事件
- collision_list = pygame.sprite.spritecollide(my_sprite, my_group, False)
- for sprite in collision_list:
pygame.event.get() 方法用于获取所有的 Pygame 事件。pygame.key.get_pressed() 方法用于检测键盘按键的状态。pygame.sprite.spritecollide() 方法用于检测精灵对象之间的碰撞。您可以根据具体情况添加其他事件处理代码。
关于精灵类,有一个地方容易迷惑,在pygame的sprite类定义中,翻看源码只有__g一个属性,它的img和rect属性哪里来的?
这是python语法中一个强大有有点儿奇怪的地方(区别于其他语言)。在python父类能够访问子类属性,即使没在父类中定义的属性。举例如下:
- print('Hello World')
- class Sprite:
- """simple base class for visible game objects
- """
- def __init__(self, *groups):
- self.__g = set() # The groups the sprite is in
- if groups:
- self.add(*groups)
- def add(self, *groups):
- """add the sprite to groups
- Sprite.add(*groups): return None
- Any number of Group instances can be passed as arguments. The
- Sprite will be added to the Groups it is not already a member of.
- """
- has = self.__g.__contains__
- for group in groups:
- if hasattr(group, "_spritegroup"):
- if not has(group):
- group.add_internal(self)
- self.add_internal(group)
- else:
- self.add(*group)
- def add_internal(self, group):
- """
- For adding this sprite to a group internally.
- :param group: The group we are adding to.
- """
- self.__g.add(group)
-
- def update(self, *args, **kwargs):
- print("Sprite update")
- print(self.image)
- print(self.rect)
- """method to control sprite behavior"""
-
-
- class Bullet(Sprite):
- def __init__(self, speed):
- super().__init__()
- self.image =4
- self.rect = 5
- self.speed = speed
-
- def update(self, *args):
- super().update()
- print("Bullet update")
-
- b = Bullet(5)
- b.update()
-
- print("over")
结果输出:
- Hello World
- Sprite update
- 4
- 5
- Bullet update
- over
在父类Sprite中并没有定义image和rect属性,但是竟还可以使用,输出了子类中赋值过的4,5。
如果在上述Bullet的update函数中,调用super.update()方法时传个参,会改变image和rect的值吗?如下:
- class Bullet(Sprite):
- def __init__(self, speed):
- super().__init__()
- self.image =4
- self.rect = 5
- self.speed = speed
-
- def update(self, *args):
- #调用父类方法并传参8,9
- super().update(8,9)
- print("Bullet update")
答案是不会改变image和rect的值的,因为在Python中 *
和 **
是用作函数参数的特殊符号,用于处理可变数量的位置参数和关键字参数。
*args
表示接受任意数量的位置参数,并将它们作为一个元组传递给函数。在这个示例中, add()
方法的 *groups
参数表示可以传入任意数量的 Group
实例,这些实例将被作为元组传递给 groups
参数。在函数内部,可以使用 groups
变量来访问传递的参数元组。
在代码中的 update()
方法中, *args
和 **kwargs
参数表示接受任意数量的位置参数和关键字参数。总结起来, *args
用于接受任意数量的位置参数, **kwargs
用于接受任意数量的关键字参数。这样可以使函数更加灵活,能够处理不同数量和类型的参数。
举例,看一下示例会输出什么:
- def update(self, *args, **kwargs):
- print("Sprite update")
- #print(self.image)
- #print(self.rect)
- print("args:")
- print(args)
- print("kwargs:")
- print(kwargs)
-
-
- update(8,9)
-
- update(8,9,a="1",b="2")
-
- update(a="1",b="2")
结果输出:
- args:
- (8, 9)
- kwargs:
- {}
-
- args:
- (8, 9)
- kwargs:
- {'a': '1', 'b': '2'}
-
- args:
- ()
- kwargs:
- {'a': '1', 'b': '2'}
以上是定义函数时参数带*或**的情况,那么可能还见到过调用函数时,传递*和**的又是什么含义呢?如调用:test(*args)
test(*args)
,星号的作用其实就是把序列 args 中的每个元素,当作位置参数传进去。比如test(*args)
这个代码,如果 args 等于 (1,2,3) ,那么这个代码就等价于 test(1, 2, 3) 。
- def foo(a,b):
- print(a)
- print(b)
-
- args = [1,2] #这里只能有两个元素
- foo(*args)
同理,调用test(**kwargs)
,双星号的作用则是把字典 kwargs 变成关键字参数传递。比如test(**kwargs)
这个代码,如果 kwargs 等于 {“a”:1,“b”:2,“c”:3} ,那这个代码就等价于 test(a=1,b=2,c=3) 。
- def foo(a,b):
- print(a)
- print(b)
-
- kwargs = {'a':1,'b':2} #这里只能是'a'和'b'
- foo(**kwargs)
猜下以下代码会输出什么?
- #示例一
- def foo(a,b,c,*args):
- print("a=%s" % (a,))
- print("b=%s" % (b,))
- print("c=%s" % (c,))
- print("args=%s" % (args,))
-
- argtuple = ("testa","testb","testc","excess1","excess2")
- foo(*argtuple)
-
- #示例二
- def foo1(a,b,c,**args):
- print("a=%s" % (a,))
- print("b=%s" % (b,))
- print("c=%s" % (c,))
- print("args=%s" % (args,))
-
- argdict = dict(a="testa", b="testb", c="testc", excessarg="string")
- foo1(**argdict)
输出:
- a=testa
- b=testb
- c=testc
- args=(‘excess’,‘excess2’)
-
- a=testa
- b=testb
- c=testc
- args={‘excessarg’: ‘string’}
再举个例子,注意观察调用方式的不同:
- def foo1(*args):
- print("args=%s" % (args,))
- print(args[0])
-
- argtuple1 = ("testa","testb","testc","excess1","excess2")
-
- foo1(argtuple1)
- foo1(*argtuple1)
foo1的两种调用一样吗?
肯定不一样有区别。记住foo1(*argtuple1)调用中,*argtuple1就相当于把tulpe中的元素给解包了。相当于调用的foo1("testa","testb","testc","excess1","excess2"),因此arg[0]输出就是testa
上述代码的执行结果:
- args=(('testa', 'testb', 'testc', 'excess1', 'excess2'),)
-
- ('testa', 'testb', 'testc', 'excess1', 'excess2')
-
- args=('testa', 'testb', 'testc', 'excess1', 'excess2')
-
- testa
- import pygame
- import random
- from pgu import gui,timer
-
- # 常量
- WIDTH, HEIGHT = 800, 600
-
- # 初始化操作
- pygame.init()
- pygame.mixer.init()
- # 创建游戏窗口
- screen = pygame.display.set_mode((WIDTH, HEIGHT))
-
- # 设置游戏标题
- pygame.display.set_caption('飞机拼音大作战--关注作者:blog.csdn.net/qq8864')
-
- # 添加音乐
- 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
- pygame.time.set_timer(CREATE_ENEMY, 2000)
-
- # 加载字体文件
- font_path = "./font/SIMYOU.ttf" # 替换为你的字体文件路径
- font_size = 24
- font = pygame.font.Font(font_path, font_size)
-
- score = 0
- score_txt = "得分:"+str(score) # 要显示的文本
- text_surface = font.render(score_txt, True, (255, 255, 255))
-
- fail_image = pygame.image.load("./image/failed.png")
- show_image = False # 控制是否显示图片
-
- tup_target = ('a','o','e','i','u','ü','b','p','m','f','d','t','n','l')
- #退出对话框
- class QuitDialog(gui.Dialog):
- def __init__(this,main):
- title = gui.Label("Dialog Box")
- gui.Dialog.__init__(this, title, main)
- def close():
- pygame.quit()
- exit()
-
-
- app = gui.App() #初始化gui
- con = gui.Container() #生成gui的容器
- quitDlg = QuitDialog(con) #生成弹窗abc
- btn = gui.Button("game over") #生成文字为a的按钮
- btn.connect(gui.CLICK, quitDlg.close, None)#将按钮与弹窗的弹出绑定
- label = gui.Label("Are you sure you want to quit?")
- quitDlg.add(label, 2, 2)
- quitDlg.add(btn,2,3) #将按钮安放在容器(0,0)位置
- app.init(con)
- # ========游戏开始页面静态效果==========
- # 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.images = [pygame.image.load('./image/planeDie' + str(i) + '.png') for i in range(1, 4)]
- self.image_index = 0
- self.readt_to_change = 0
- self.dired = 0 #默认三条命
- 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
-
- if self.dired != 0:
- if self.image_index <= 2:
- self.readt_to_change += 1
- if self.readt_to_change % 4 == 0:
- self.image = self.images[self.image_index]
- self.image_index += 1
- else:
- self.image_index = 0
- self.kill()
-
-
- 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):
- count_ = 0
- def __init__(self, speed):
- super().__init__()
- Enemy.count_ += 1
- self.image = pygame.image.load('./image/'+'enemy'+str( Enemy.count_)+".png")
- self.rect = self.image.get_rect()
- self.rect.x = 800
- self.rect.y = random.randint(0, HEIGHT)
- self.speed = speed
- self.id = Enemy.count_
- self.name = 'enemy'+str( Enemy.count_)
- print("self.id=",self.id)
- print("Enemy.count=",Enemy.count_)
- if Enemy.count_ >= 50:
- Enemy.count_=0
-
- 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/background1.jpg')
- self.rect = self.image.get_rect()
- self.ready_to_move = 0
- self.index = 1
-
- def update(self, *args):
- self.rect.x -= 3
- if self.rect.right <= 0:
- self.index+=1
- self.image = pygame.image.load('./image/background'+str(self.index)+'.jpg')
- self.rect = self.image.get_rect()
- self.rect.x = self.rect.width
- if self.index == 4:
- self.index = 3
-
-
- # 初始化精灵组
- 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, 4)))
- app.event(event) #将pygame的事件传递给pgu,很重要
-
- # 碰撞检测,返回字典,得到二者信息
- 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)
- print("enemy.id:"+str(enemy.id))
- score+=1
-
- # 碰撞检测,返回字典,得到二者信息
- collision1 = pygame.sprite.groupcollide(enemy_sprite, hero_sprite, True, False)
- for enemy in collision1.keys():
- hero1.dired += 1
- quitDlg.open()
- show_image = True
- break
- #explode = Explode()
- #explode.rect = enemy.rect
- #explode_sprite.add(explode)
- #quitDlg.open()
-
- # screen.fill((0,0,0))
- for group in [bg_sprite, hero_sprite, enemy_sprite, bullet_sprite, explode_sprite]:
- group.update()
- group.draw(screen)
- #screen.fill((0,0,0)) #生成一个屏幕
- score_txt = "得分:"+str(score)
- text_surface = font.render(score_txt, True, (255, 0, 255))
- screen.blit(text_surface, (680, 10))
-
- if show_image:
- screen.blit(fail_image, (300, 100)) # 绘制图像
-
- app.paint()
- pygame.display.update()
- #app.paint() #将pgu容器的内容画出
一个好玩的游戏肯定少不了精美的图片和声音的制作。这里制作背景图片,从网上精选了一些好看且震撼的宇宙图片作为背景。其它拼音字母则从网上截取好看的彩色的拼音字母。
这里着重说下拼音发音文件的制作,网上几乎很难搜到现成的汉语拼音的单个的wav文件。这里介绍两种办法,一种是找到涵盖所有正规完整拼音字母发音的音频文件,使用goldwave软件进行剪辑,优点是发音标准些,但是剪辑麻烦些,需要从里面去找对应的音频。
还有一种方法是使用科大讯飞的离线语音合成技术合成语音。如输入字母'a' ,让其合成发音。但是我试过了发现竟然是英文。不过还是有办法啦,直接输入a - 阿 o - 哦 e - 额 i - 一 u - 乌对应的汉字即可,合成整个语音,然后再通过goldwave软件裁剪。
const char* text = "啊,我,额,一,无,与,波,破,摸,佛,的,特,呢,了,个,可,和,及,器,洗,之,吃,时,日,子,此,思,一,无"; //合成文本
比如使用科大讯飞的离线语言合成技术合成上面这么一段话,逗号作为分隔。然后使用goldwave音频裁剪制作工具,对每段音频进行裁剪和保存成拼音字母的wav格式音频文件。然而发音可能因为音调的原因不标准,所以尽量找发音准且音调为一声的字。
我找的发音最接近的如下:
- 韵母:a, o, e, i, u, ü, ai, ei, ui, ao, ou, iu, ie, üe, er, an, en, in, un, ün, ang, eng, ing, ong
-
- 阿,窝,额,一,屋,迂,挨,诶,微,凹,欧,优,页,约,儿,安,恩,音,温,晕,盎,鞥,英,翁
-
- 声母:b, p, m, f, d, t, n, l, g, k, h, j, q, x, zh, ch, sh, r, z, c, s ,y,w
-
- 播,泼,摸,佛,的,特,呢,嘞,哥,蝌,喝,机,其,西,织,吃,狮,日,资,疵,丝,衣,屋
剪辑也很方便,因为有逗号分隔,以下每个小绿色的波形都是一段发音文件。
声音合成的c代码:
- /*
- * 语音合成(Text To Speech,TTS)技术能够自动将任意文字实时转换为连续的
- * 自然语音,是一种能够在任何时间、任何地点,向任何人提供语音信息服务的
- * 高效便捷手段,非常符合信息时代海量数据、动态更新和个性化查询的需求。
- */
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <errno.h>
- #include "../../include/qtts.h"
- #include "../../include/msp_cmn.h"
- #include "../../include/msp_errors.h"
- typedef int SR_DWORD;
- typedef short int SR_WORD ;
-
- /* wav音频头部格式 */
- typedef struct _wave_pcm_hdr
- {
- char riff[4]; // = "RIFF"
- int size_8; // = FileSize - 8
- char wave[4]; // = "WAVE"
- char fmt[4]; // = "fmt "
- int fmt_size; // = 下一个结构体的大小 : 16
-
- short int format_tag; // = PCM : 1
- short int channels; // = 通道数 : 1
- int samples_per_sec; // = 采样率 : 8000 | 6000 | 11025 | 16000
- int avg_bytes_per_sec; // = 每秒字节数 : samples_per_sec * bits_per_sample / 8
- short int block_align; // = 每采样点字节数 : wBitsPerSample / 8
- short int bits_per_sample; // = 量化比特数: 8 | 16
-
- char data[4]; // = "data";
- int data_size; // = 纯数据长度 : FileSize - 44
- } wave_pcm_hdr;
-
- /* 默认wav音频头部数据 */
- wave_pcm_hdr default_wav_hdr =
- {
- { 'R', 'I', 'F', 'F' },
- 0,
- {'W', 'A', 'V', 'E'},
- {'f', 'm', 't', ' '},
- 16,
- 1,
- 1,
- 16000,
- 32000,
- 2,
- 16,
- {'d', 'a', 't', 'a'},
- 0
- };
- /* 文本合成 */
- int text_to_speech(const char* src_text, const char* des_path, const char* params)
- {
- int ret = -1;
- FILE* fp = NULL;
- const char* sessionID = NULL;
- unsigned int audio_len = 0;
- wave_pcm_hdr wav_hdr = default_wav_hdr;
- int synth_status = MSP_TTS_FLAG_STILL_HAVE_DATA;
-
- if (NULL == src_text || NULL == des_path)
- {
- printf("params is error!\n");
- return ret;
- }
- fp = fopen(des_path, "wb");
- if (NULL == fp)
- {
- printf("open %s error.\n", des_path);
- return ret;
- }
- /* 开始合成 */
- sessionID = QTTSSessionBegin(params, &ret);
- if (MSP_SUCCESS != ret)
- {
- printf("QTTSSessionBegin failed, error code: %d.\n", ret);
- fclose(fp);
- return ret;
- }
- ret = QTTSTextPut(sessionID, src_text, (unsigned int)strlen(src_text), NULL);
- if (MSP_SUCCESS != ret)
- {
- printf("QTTSTextPut failed, error code: %d.\n",ret);
- QTTSSessionEnd(sessionID, "TextPutError");
- fclose(fp);
- return ret;
- }
- printf("正在合成 ...\n");
- fwrite(&wav_hdr, sizeof(wav_hdr) ,1, fp); //添加wav音频头,使用采样率为16000
- while (1)
- {
- /* 获取合成音频 */
- const void* data = QTTSAudioGet(sessionID, &audio_len, &synth_status, &ret);
- if (MSP_SUCCESS != ret)
- break;
- if (NULL != data)
- {
- fwrite(data, audio_len, 1, fp);
- wav_hdr.data_size += audio_len; //计算data_size大小
- }
- if (MSP_TTS_FLAG_DATA_END == synth_status)
- break;
- }
- printf("\n");
- if (MSP_SUCCESS != ret)
- {
- printf("QTTSAudioGet failed, error code: %d.\n",ret);
- QTTSSessionEnd(sessionID, "AudioGetError");
- fclose(fp);
- return ret;
- }
- /* 修正wav文件头数据的大小 */
- wav_hdr.size_8 += wav_hdr.data_size + (sizeof(wav_hdr) - 8);
-
- /* 将修正过的数据写回文件头部,音频文件为wav格式 */
- fseek(fp, 4, 0);
- fwrite(&wav_hdr.size_8,sizeof(wav_hdr.size_8), 1, fp); //写入size_8的值
- fseek(fp, 40, 0); //将文件指针偏移到存储data_size值的位置
- fwrite(&wav_hdr.data_size,sizeof(wav_hdr.data_size), 1, fp); //写入data_size的值
- fclose(fp);
- fp = NULL;
- /* 合成完毕 */
- ret = QTTSSessionEnd(sessionID, "Normal");
- if (MSP_SUCCESS != ret)
- {
- printf("QTTSSessionEnd failed, error code: %d.\n",ret);
- }
-
- return ret;
- }
-
- int main(int argc, char* argv[])
- {
- int ret = MSP_SUCCESS;
- const char* login_params = "appid = 99999, work_dir = .";//登录参数,appid与msc库绑定,请勿随意改动
- /*
- * rdn: 合成音频数字发音方式
- * volume: 合成音频的音量
- * pitch: 合成音频的音调
- * speed: 合成音频对应的语速
- * voice_name: 合成发音人
- * sample_rate: 合成音频采样率
- * text_encoding: 合成文本编码格式
- *
- */
- const char* session_begin_params = "engine_type = local,voice_name=xiaoyan, text_encoding = UTF8, tts_res_path = fo|res/tts/xiaoyan.jet;fo|res/tts/common.jet, sample_rate = 16000, speed = 50, volume = 50, pitch = 50, rdn = 2";
- const char* filename = "tts_sample.wav"; //合成的语音文件名称
- const char* text = "啊,我,额,一,无,与,波,破,摸,佛,的,特,呢,了,个,可,和,及,器,洗,之,吃,时,日,子,此,思,一,无"; //合成文本
- /* 用户登录 */
- ret = MSPLogin(NULL, NULL, login_params); //第一个参数是用户名,第二个参数是密码,第三个参数是登录参数,用户名和密码可在http://www.xfyun.cn注册获取
- if (MSP_SUCCESS != ret)
- {
- printf("MSPLogin failed, error code: %d.\n", ret);
- goto exit ;//登录失败,退出登录
- }
-
- printf("\n###########################################################################\n");
- printf("###########################################################################\n\n");
-
- /* 文本合成 */
- printf("开始合成 ...\n");
- ret = text_to_speech(text, filename, session_begin_params);
- if (MSP_SUCCESS != ret)
- {
- printf("text_to_speech failed, error code: %d.\n", ret);
- }
- printf("合成完毕\n");
-
- exit:
- printf("按任意键退出 ...\n");
- getchar();
- MSPLogout(); //退出登录
-
- return 0;
- }
对应的makefile文件:
- #common makefile header
-
- DIR_INC = ../../include
- DIR_BIN = ../../bin
- DIR_LIB = ../../libs
-
- TARGET = tts_offline_sample
- BIN_TARGET = $(DIR_BIN)/$(TARGET)
-
- CROSS_COMPILE =
- CFLAGS = -g -Wall -I$(DIR_INC)
-
- ifdef LINUX64
- LDFLAGS := -L$(DIR_LIB)/x64
- else
- LDFLAGS := -L$(DIR_LIB)/x86
- endif
- LDFLAGS += -lmsc -lrt -ldl -lpthread -lstdc++
-
- OBJECTS := $(patsubst %.c,%.o,$(wildcard *.c))
-
- $(BIN_TARGET) : $(OBJECTS)
- $(CROSS_COMPILE)gcc $(CFLAGS) $^ -o $@ $(LDFLAGS)
-
- %.o : %.c
- $(CROSS_COMPILE)gcc -c $(CFLAGS) $< -o $@
- clean:
- @rm -f *.o $(BIN_TARGET)
-
- .PHONY:clean
-
- #common makefile foot
以下是一般的使用流程:
1. 设置Pygame:使用pip安装Pygame库,并在Python脚本中导入它。
2. 初始化Pygame:使用 pygame.init()
初始化Pygame模块。
3. 设置游戏窗口:使用 pygame.display.set_mode()
创建游戏窗口。设置窗口大小、标题和其他属性。
4. 游戏循环:创建一个循环来持续运行游戏。该循环处理用户输入、更新游戏逻辑和渲染游戏图形。
5. 处理事件:在游戏循环内部,使用 pygame.event.get()
处理各种事件,如键盘或鼠标输入。根据游戏的需求响应这些事件。
6. 更新游戏状态:根据用户输入和其他因素更新游戏状态。包括移动游戏对象、检测碰撞、更新得分等。
7. 渲染图形:使用 pygame.Surface
和 pygame.draw
函数将游戏图形渲染到游戏窗口上。包括绘制游戏对象、背景、文本和其他可视元素。
8. 显示更新:使用 pygame.display.flip()
或 pygame.display.update()
更新游戏窗口,显示当前帧的变化。
9. 游戏逻辑:实现特定于游戏的逻辑、规则和机制。这可能包括关卡、得分、道具、敌人AI等。
10. 退出游戏:处理游戏退出事件,并使用 pygame.quit()
清理资源。
以下是一个简单的Pygame游戏示例,显示一个窗口和一个移动的角色:
- import pygame
- from pygame.locals import *
-
- # 初始化Pygame
- pygame.init()
-
- # 设置游戏窗口
- screen = pygame.display.set_mode((800, 600))
- pygame.display.set_caption('我的游戏')
-
- # 游戏循环
- running = True
- while running:
- # 处理事件
- for event in pygame.event.get():
- if event.type == QUIT:
- running = False
-
- # 更新游戏状态
-
- # 渲染图形
- screen.fill((0, 0, 0)) # 用黑色填充屏幕
- pygame.draw.rect(screen, (255, 0, 0), (100, 100, 50, 50)) # 在(100, 100)处绘制一个红色矩形
-
- # 显示更新
- pygame.display.flip()
-
- # 退出游戏
- pygame.quit()
最后,需要完整可运行程序的,请在首页添加作者微信或者关注公众号留言索取。
python+pygame游戏开发之使用Py2exe打包游戏-腾讯云开发者社区-腾讯云
6种打包Python代码的方法,让你的程序变成exe应用!_python打包exe_JPX-NO的博客-CSDN博客
pip国内镜像源-Python安装第三方库(一篇足以、都是干货) - 知乎
python--pygame实现各级菜单栏目设置_pygame 下拉菜单_DY.work的博客-CSDN博客
python玄阶斗技--tkinter库_马骁尧的博客-CSDN博客
Python pygame(GUI编程)模块最完整教程(1)_pygame模块详解_Python-ZZY的博客-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。