欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python游戏引擎ppb介绍(6)声音、文字与自定义事件

程序员文章站 2022-07-12 23:43:27
...

ppb播放声音的机制很简明,但相对而言显示文字的操作略显繁琐,可能这个引擎的设计者的本意关注的是更形象化的视觉效果。虽说一图胜千言,但总也有时候,文字是必不可少的。

修改文字,我们使用自定义事件机制。这也是ppb引擎中一个比较有特色的实现。

python游戏引擎ppb介绍(6)声音、文字与自定义事件

声音的播放

我们希望在两个地方加入声音效果,一是导弹发射的瞬间,我们加入的音效;另一个是靶子爆炸的瞬间,我们加入爆炸音效。音效文件本身无法在网页中提供,请朋友去爱给音效自行下载,或者留言向一声索取。

发射导弹的代码是Blob类中的鼠标事件(on_button_pressed

    def on_button_pressed(self, event, signal):

        if event.button== ppb.buttons.Primary:

           p1= (event.position- self.position).normalize()

           event.scene.add(Projectile(position=self.position, direction= p1))

           signal(ppb.events.PlaySound(ppb.Sound('resources/sou.mp3')))

 

我们看到了第三个参数(signal)的用途之一:播放声音。其实这个参数的功能本质是用来发各种消息,我们很快会看到它另外的用途。

播放爆炸声,则放在靶子碰撞检测的地方。我们一会儿再看。

文字显示

文字在ppb中,也被当作一种精灵来处理,唯一的区别是这个精灵的图片是文字。

游戏标题的文字类,我们把它命名为label1

class label1(ppb.Sprite):

    image= ppb.Text('射击游戏',font=ppb.Font("resources/happy1.ttf", size=62), color=(255,255,100))

    position= ppb.Vector(0,5)

    layer= 1

 

文字通过ppb.Text()来创建,这几个属性是必须的:文本内容、字体文件、字体大小、颜色。

  • 文本内容:文本内容是无法单独更改的,想改变它就得重建图片,详见下。
  • 字体文件TrueType文件,目前只能通过文件的形式来指定字体,而不能直接用系统字体。
  • 字体大小:字体本身有大小,同时作为文本精灵还有一个大小,两者都会影响实际文字的显示尺寸。但我们应当尽可能使用字体本身的大小,让精灵大小保持为1,这样字体显示更清晰。
  • 颜色:一个三元组,标明了RGB三个颜色成分,取值范围都是0~255

游戏积分的文字类,我们把它命名为label2

class label2(ppb.Sprite):

    image= ppb.Text('score: 0',font=ppb.Font("resources/happy1.ttf", size=62), color=(255,255,100))

    position= ppb.Vector(0,1)

    score= 0

    layer= 1

    def on_change_score(self, event, signal):

        self.image= ppb.Text('score: '+ str(event.score),font=ppb.Font("resources/happy1.ttf", size=62), color=(255,255,100))

 

既然文字也是一个精灵,它就可以自己处理事件。我们在这个on_change_score事件中改变文字的内容。这个事件是怎么触发的?

python游戏引擎ppb介绍(6)声音、文字与自定义事件

自定义事件

ppb中,自定义事件非常简单。新建一个即可。比如上面的on_change_text事件,它的类是什么样的呢?

class ChangeScore:

    def __init__(self, score):

      self.score= score

 

ppb中约定,类名如果使用驼峰命名法(每个单词的头一个字母大写),所对应的事件名就是转换为蛇形命名法(全小写,用下划线分割单词)之后的样子。

一个事件定义好,所有精灵均可触发它,也可以响应它。

比如当靶子被击中的时候,分数增加20分。我们在Target的刷新事件(on_update)中,增加一些代码。

    def on_update(self, update_event, signal):

       global score

       self.rotation-=self.speed* update_event.time_delta

       for p in update_event.scene.get(kind=Projectile):

           if (p.position - self.position).length <= self.size/ 2:

               update_event.scene.add(BombImg(position=self.position))

               update_event.scene.remove(self)

               update_event.scene.remove(p)

               score+= 20

               signal(ppb.events.PlaySound(ppb.Sound('resources/bomb.mp3')))

               signal(ChangeScore(score=score))

               break

 

score是一个全局变量,严谨地看,仅仅是为了计分就使用一个全局变量是不合理的,但在我们这个小例子中,暂时这样处理也没有什么问题。当一个靶子被击中,分数增加20。随后通过调用signal参数,触发两个事件,一个是播放声音,另一个就是修改计分。

signal参数是一个函数,传递一个自定义事件类的实例,系统会自动产生一个事件。如果某个精灵定义了响应它的函数,就会得到这个事件,并且获得相应的参数。

我们可以想象,其实播放声音也是同样的原理,是系统内某个地方,接收了这个PlaySound事件并播放声音。这种事件传播机制对于系统解耦很有利。

python游戏引擎ppb介绍(6)声音、文字与自定义事件

初始化部分的修改

在场景布局时,增加这两个文本精灵即可。

def setup(scene):

    scene.add(Blob(pos=(0, -3.5)))

    scene.add(label1())

    scene.add(label2())

    for x in range(-4, 5, 2):

       scene.add(Target(position=ppb.Vector(x, 3)))

 

注意到两个文本的位置,已经在类中固定,所以在布局时无须再次指定。

完整代码

到此,我们已经用ppb开发完成了一个小游戏,在过程中介绍了ppb引擎的若干核心机制。下次课也就是最后一次关于ppb引擎的课,我们将补充介绍一下它的相机机制,并对这个引擎的功能和使用做一个总结。

下面贴完整代码

import ppb

from ppb.features.animation import Animation



score= 0



class Blob(ppb.Sprite):

    image = Animation("resources/blob_{0..6}.png", 10)

    layer= 3



    def on_button_pressed(self, event, signal):

        if event.button== ppb.buttons.Primary:

           p1= (event.position- self.position).normalize()

           event.scene.add(Projectile(position=self.position, direction= p1))

           signal(ppb.events.PlaySound(ppb.Sound('resources/sou.mp3')))



    def on_mouse_motion(self, event, signal):

        p1= (event.position- self.position).normalize()

        self.rotation= 270-p1.angle(ppb.Vector(1,0))



class Target(ppb.Sprite):

    image = ppb.Image("resources/Target.png")

    speed= 50

    layer= 2

    def on_update(self, update_event, signal):

       global score

       self.rotation-=self.speed* update_event.time_delta

       for p in update_event.scene.get(kind=Projectile):

           if (p.position - self.position).length <= self.size/ 2:

               update_event.scene.add(BombImg(position=self.position))

               update_event.scene.remove(self)

               update_event.scene.remove(p)

               score+= 20

               signal(ppb.events.PlaySound(ppb.Sound('resources/bomb.mp3')))

               signal(ChangeText(score=score))

               break



class ChangeText:

    def __init__(self, score):

      self.score= score



class BombImg(ppb.Sprite):

    layer= 1

    image = Animation("resources/bomb_{0..15}.png", 16)

    def on_update(self, update_event, signal):

        if self.image.current_frame==15:

            update_event.scene.remove(self)



class Projectile(ppb.Sprite):

    image = ppb.Image("resources/Projectile.png")

    speed = 6

    layer= 2



    def on_update(self, update_event, signal):

       self._rotation= 90- self.direction.angle(ppb.Vector(1,0))

       self.position += self.direction * self.speed * update_event.time_delta



class label1(ppb.Sprite):

    image= ppb.Text('射击游戏',font=ppb.Font("resources/happy1.ttf", size=62), color=(255,255,100))

    position= ppb.Vector(0,5)

    layer= 1



class label2(ppb.Sprite):

    image= ppb.Text('score: 0',font=ppb.Font("resources/happy1.ttf", size=62), color=(255,255,100))

    position= ppb.Vector(0,1)

    score= 0

    layer= 1

    def on_change_text(self, event, signal):

        self.image= ppb.Text('score: '+ str(event.score),font=ppb.Font("resources/happy1.ttf", size=62), color=(255,255,100))



def setup(scene):

    scene.add(Blob(pos=(0, -3.5)))

    scene.add(label1())

    scene.add(label2())

    for x in range(-4, 5, 2):

       scene.add(Target(position=ppb.Vector(x, 3)))





ppb.run(setup=setup,resolution=(800, 600))