1.5.2 将小球反弹回来
下面考虑这样一种规则,那就是当小球超出窗口边界后,将其反弹到窗口之内。例如,小球如果向右边移动时超出了边界,则让它掉过头来朝左移动。对其他方向也可采取类似的操作。这就跟桌球运动中的小球一样,若在移动中碰到库边则发生反弹。效果如图1.9所示。这样的设计看起来还蛮有趣的,不是吗?赶紧编写代码吧。
首先实现窗口右边界的反弹。将update()修改为如下代码:
我们把这段代码“翻译”一下,它表示:小球的横坐标先是增加5个单位,如果它的右边界超出了窗口右边,则将它的横坐标减少5个单位。看起来完全符合逻辑,那么运行看看是什么效果。
图1.9 小球反弹效果示意图
怎样,是不是有点失望?实际情况是小球一动不动地停靠在窗口右边。为何会这样呢?让我们仔细分析一下程序。你可别忘了,update()函数是在游戏循环中反复调用执行的,因此小球的x值会不断增加,当x值大于窗口宽度之后会执行一次x值减5操作。而在下一次的循环中,小球的x值仍然要加5,增加之后超出了边界随即又减5。最后的结果就如同你所见到的,小球的坐标既不能继续增加,也不会持续减少,它永远地停靠在窗口右边界。
以上代码的问题在于,我们希望小球向右移动超出边界后“反弹”,也就是掉转移动的方向,改为向左移动。但是从代码来看,“ball.x += 5”这句却让小球一直朝右移动。原因就在于,为小球的x坐标值增加的是一个常数5,这个常数仅仅代表了移动的距离,而不能表示移动的方向。为了实现小球的反弹,需要让小球的坐标加上一个变量,这个变量既能表示移动的距离,又能表示移动的方向,将其称为速度变量。
由于小球可以在水平和垂直两个方向移动,可以定义两个速度变量dx和dy,分别表示小球在水平和垂直方向的速度。dx和dy的大小用来表示移动距离,而正负则可表示不同的方向,例如dx为5表示向右移动5个单位,而-5表示向左移动5个单位;相应地,dy为5表示向下移动5个单位,而-5表示向上移动5个单位。
另外,由于速度是专属于小球的变量,因此最好将其设置为小球对象的属性。需要注意的是,Pgzero事先并没有为角色设置速度这个属性,所以需要我们自己添加。其实在Python语言中为一个对象添加属性非常方便,可以采用如下的代码为小球添加速度属性:
这样一来,就能方便地实现小球的反弹了。只需要将update()函数修改为如下形式即可:
运行一下,看看是不是可以了呢?当然,以上代码只能实现窗口右边界的反弹,如果要让小球在窗口左边界也能反弹,则if语句中还要增加额外的判断条件,即判断小球的左边界是否超出了窗口的左边界。可以用or关键字来连接两个不同的判断条件,则不论是超出右边界还是超出左边界,小球都将发生反弹。代码如下所示:
这样一来,小球向左移动时,如果左边界值小于0之后,也会掉转方向,重新朝右方移动。于是游戏的运行效果就是,小球不停地在窗口左右两侧之间来回移动。想一想,若要小球在窗口上下边界之间来回移动,又该如何编写代码呢?
最后,让小球的垂直速度也同时发生改变,并添加垂直方向的反弹规则。代码如下所示:
运行一下看看,现在小球竟然围绕窗口四周愉快地弹跳起来了!是不是很神奇呢?