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

python游戏引擎ppb介绍(4)开炮:精灵的创建与移动

程序员文章站 2022-03-20 11:21:30
...

发射导弹,其实就是创建一个不断移动的新的精灵。

本节课讲精灵的创建和它的自动平移,介绍层的概念,并开始使用资源路径。

python游戏引擎ppb介绍(4)开炮:精灵的创建与移动

发射子弹

Projectile是导弹的意思。我们建立第三个精灵类:Projectile

python游戏引擎ppb介绍(4)开炮:精灵的创建与移动

Projectile.png

它的图片看起来是比较抽象的。

与前两个精灵相同,我们只需建立一个同名的类就可以默认关联这个图片,但这次我们想显式地指定图片。这是因为我们不希望所有的图片都和源代码放在一起。我们在代码目录中建立一个子目录resources,并且把所有的图片都移动到这个目录中。这样,我们就必须显式地指定精灵的图片,其实也很简单,如下:

class Projectile(ppb.Sprite):

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

    speed = 6

    layer= 2

    def on_update(self, event, signal):

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

 

其中,speed=6代表导弹的飞行速度,每秒飞行6个距离单位,考虑到屏幕横宽为25个距离单位,这个速度已经比较快了。

我们看导弹的on_update函数,它根据速度(speed)和方向(direction)改变导弹的位置。仍然使用的是向量计算,让代码显得简洁而清晰。

这里的方向(direction)不是精灵的内置属性(相关的内置属性是rotation)。它和speed一样,是一个扩展属性。direction属性并没有初始值,它将在发射(创建)时指定,并保存不变。

我们注意到导弹类中还定义了一个新增的参数:层号(layer)。当精灵有重叠的时候,layer值大的精灵覆盖layer值较小的精灵。我们可以简单地理解为每个精灵在显示时是按照layer从小到大排序的,layer值小的,在屏幕中先显示;layer值大的后显示,所以会在上面。

python游戏引擎ppb介绍(4)开炮:精灵的创建与移动

鼠标点击事件

我们希望当鼠标左键点击时,发生一枚导弹。只需给Blob类新增一个响应鼠标按下的函数:

class Blob(ppb.Sprite):

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

    layer= 3

    def on_mouse_motion(self, event, signal):

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

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

    # 新增的函数

    def on_button_pressed(self, event, signal):

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

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

            p2= 90- p1.angle(ppb.Vector(1,0))

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

 

on_button_pressed事件,接收鼠标按下事件。对于更精细的交互处理,我们可能还会涉及到鼠标抬起事件——严格来说按下并抬起才能称为一次点击。但在这里,按下鼠标就发射导弹,仍然是很清晰的操作逻辑,对我们没有什么影响。

鼠标按下事件的event中,包含了究竟按下的是哪个按钮的信息,保存在event.button中,一共有三种可能性:

  • ppb.buttons.Primary
  • ppb.buttons.Secondary
  • ppb.buttons.Tertiary

分别代表主键(默认为左键)、辅键(默认为右键)、第三键(默认为中间键)。这里包含了一个扩展性是,如果你机器上的设置不同,它会跟随改变。

下面的计算,又是向量计算,分别计算出导弹的转向角(rotation)和前进方向(direction)。

在这里,direction的归一化处理normalize()就是必须的,因为在导弹代码中,我们用它和速度的乘积来计算新的位置,它的模如果不是1,会干扰速度的真实值。

我们注意观察导弹的图片,它的方向是向下的,所以它的值与导弹图片的前进方向是有一个固定差距的。

event中另外包含了一个关键信息,就是场景本身。通过这个参数,可以在场景中新建一个导弹精灵,就如同在setup函数中所做的一样。

场景布局与层号的作用

def setup(scene):

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

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

       scene.add(Target(layer=(x+4)/2,position=ppb.Vector(x, 3)))

 

我们在布置屏幕的时候,给每个靶子设置了不同的层号。如上代码的5个靶子,从左到右依次是0,1,2,3,4。我们会发现,导弹永远在左边两个靶子上面,永远在右边两个靶子下面,但对于中间那个层号相同的靶子,效果则是随机的,有时在上,有时在下!

所以,如果我们希望两个精灵在交叠时有一个明确的表现,最好是设置一个确定的层号。

完整代码

下次课,我们来处理导弹击中靶子后的效果。现在,看一下目前为止的完整的源代码。

我们已经将图片放在了子目录下,所以每个类都做了响应的修改。

import ppb

from ppb.features.animation import Animation



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()

            p2= 90- p1.angle(ppb.Vector(1,0))

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



    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

    def on_update(self, update_event, signal):

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





class Projectile(ppb.Sprite):

    size = 1

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

    speed = 6

    layer= 2



    def on_update(self, event, signal):

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





def setup(scene):

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

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

       scene.add(Target(layer=(x+4)/2,position=ppb.Vector(x, 3)))





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

 

python游戏引擎ppb介绍(4)开炮:精灵的创建与移动