python游戏引擎ppb介绍(4)开炮:精灵的创建与移动
发射导弹,其实就是创建一个不断移动的新的精灵。
本节课讲精灵的创建和它的自动平移,介绍层的概念,并开始使用资源路径。
发射子弹
Projectile是导弹的意思。我们建立第三个精灵类:Projectile。
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值大的后显示,所以会在上面。
鼠标点击事件
我们希望当鼠标左键点击时,发生一枚导弹。只需给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))
上一篇: 作业九
下一篇: 3D游戏——AR图片识别与建模