(首页)开始编程之旅 翻译自Lee Harr的Start Programming

本文是使用pygsear+pygame作为开发环境,以初级用户角度来分步分阶段学习PYTHON基本概念,并以小游戏开发项目为具体案例,介绍的十分详细。编写风格清新朴实,没有象一般教科书那样枯燥,极其适合初级用户来激发兴趣时使用。

1. StartProgramming-4-3 Game

In the simplest case, every Drawable object is a rectangle.

在这个简单的例子中,每个可画的对象都是长方形的。

Even when the image that you see is not just a rectangle, the sprite is considered to be the rectangle which would cover the entire image. pygsear defines two different colors you can use for transparency, or it can use the transparency created by an image program like GIMP.

即使你看到的图像正好不是个正方形,它们的影子也将被转化成完整的图片当作长方形。 pygsear 定义了你可以用于透明的两个不同的颜色,或者你可以用象GIMP那样的图形程序创建透明图形。

Upload new attachment "image_sprite.png"

A collision is when two of these rectangles overlap.

当这些长方形重叠时就会有一个碰撞。

Upload new attachment "overlap_collision.png"

Notice here that even though the images themselves do not touch, these two sprites are colliding. In most cases this will not be a problem, since sprites tend to be small and move relatively fast.

注意这里尽管这些图片自身并没有接触,这些影子是碰到了。在大多数情况下这样做不会产生错误,因为影子一般是倾向比较的小并且移动相对比较快。

In Pong the only collision we are worried about is between the Paddle and the Ball.

在 Pong 中我们担心的仅有的一次碰撞是在 PaddleBall 之间。

   1 class Ball(Square):
   2     def hit(self):
   3         vx, vy = self.path.get_velocity()
   4         vx = abs(vx)
   5         self.path.set_velocity(vx=vx)
   6 
   7 class Pong(Game):
   8     def initialize(self):
   9         paddle = Paddle()
  10         self.sprites.add(paddle)
  11         ball = Ball()
  12         self.sprites.add(ball)
  13 
  14         self.events.add(KEYDOWN_Event(key=K_UP, callback=paddle.up))
  15         self.events.add(KEYUP_Event(key=K_UP, callback=paddle.noup))
  16 
  17         self.events.add(KEYDOWN_Event(key=K_DOWN, callback=paddle.down))
  18         self.events.add(KEYUP_Event(key=K_DOWN, callback=paddle.nodown))
  19 
  20         self.paddle = paddle
  21         self.ball = ball
  22 
  23     def checkCollisions(self):
  24         if self.ball.collide(self.paddle):
  25             self.ball.hit()

Each time through the mainloop() loop, the Game will call checkCollisions()

每次经过 mailloop() 循环,游戏将调用 checkCollisions() 过程

Our checkCollisions() will check to see if the Ball has hit the Paddle and if so, it will call the hit() for the Ball.

我们的 checkCollisions() 将检查并发现 Ball 是否撞击到了 Paddle 上,如果是它将为 Ball 调用 hit() 函数。

Important: Notice how we need to modify the initialize() method to keep a handle on the Ball and the Paddle. If the Game did not hold on to those, it would have no way of checking the collisions.

重要:注意我们如何需要修改 initialize() 方法来保持一个句柄在 BallPaddle 中。如果游戏不能保持住这些,它将没有办法检测到碰撞。

Once we know there has been a collision, we know the Ball should end up moving to the right, and so that is all hit() does. It makes sure vx is positive, using the absolute value function.

一旦我们知道这里发生了一次碰撞,我们知道 Ball 最终将向右面移动,并且那是 hit() 所做的事。它使得 vx 是正值,用绝对值函数。

The only thing left is to check for the Ball exiting the screen to the left, and to show the score.

剩下的最后一件事就是检查当球从左边飞出了屏幕,则显示得分。

   1 from pygsear.Drawable import Score
   2 
   3 class Pong(Game):
   4     def initialize(self):
   5         self.window.border(left=0, top=5, right=5, bottom=5)
   6         self.window.setTitle('Pong!')
   7 
   8         paddle = Paddle()
   9         self.sprites.add(paddle)
  10         ball = Ball()
  11         self.sprites.add(ball)
  12 
  13         self.score = Score()
  14         self.sprites.add(self.score)
  15 
  16         self.events.add(KEYDOWN_Event(key=K_UP, callback=paddle.up))
  17         self.events.add(KEYUP_Event(key=K_UP, callback=paddle.noup))
  18 
  19         self.events.add(KEYDOWN_Event(key=K_DOWN, callback=paddle.down))
  20         self.events.add(KEYUP_Event(key=K_DOWN, callback=paddle.nodown))
  21 
  22         self.paddle = paddle
  23         self.ball = ball
  24 
  25     def checkCollisions(self):
  26         if self.ball.collide(self.paddle):
  27             self.ball.hit()
  28             self.score.addPoints(1)
  29             self.score.updateScore()
  30 
  31         if not self.ball.onscreen(left=10):
  32             self.ball.center()
  33             self.ball.path.set_velocity(vx=150, vy=100)

Actually, while I was in there, I also set a title for the window and added a border around the screen.

实际上,直到现在,我也设置窗口的标题并为屏幕四周加上边框。

That border might make it a bit more clear why all of the onscreen() calls were checking for the ball to be 5 pixels from the edge.

边框能够使为什么 onscreen() 调用检查球距离边缘是5像素变得更清楚。

Here is the full source for the game. You can click on the link to download the file:

这里是游戏的完整代码。你可以点击链接下载文件:

   1 # Pong-1.0.py
   2 from pygsear.Drawable import Rectangle, Square
   3 from pygsear.Widget import Score
   4 from pygsear.Game import Game
   5 from pygsear.Event import KEYDOWN_Event, KEYUP_Event
   6 
   7 from pygame.locals import K_UP, K_DOWN
   8 
   9 class Paddle(Rectangle):
  10     def __init__(self):
  11         Rectangle.__init__(self, width=15, height=50)
  12         self.center(x=10)
  13 
  14         self.up_pressed = 0
  15         self.down_pressed = 0
  16 
  17     def up(self, ev):
  18          self.up_pressed = 1
  19 
  20     def noup(self, ev):
  21          self.up_pressed = 0
  22 
  23     def down(self, ev):
  24          self.down_pressed = 1
  25 
  26     def nodown(self, ev):
  27          self.down_pressed = 0
  28 
  29     def setVel(self):
  30         if self.up_pressed and not self.down_pressed:
  31             self.path.set_velocity(vy=-100)
  32         elif self.down_pressed and not self.up_pressed:
  33             self.path.set_velocity(vy=100)
  34         else:
  35             self.path.set_velocity(vy=0)
  36 
  37     def move(self):
  38         self.setVel()
  39         Rectangle.move(self)
  40         self.onscreen(top=-5, bottom=-5, jail=1)
  41 
  42 
  43 class Ball(Square):
  44     def __init__(self):
  45         Square.__init__(self, side=15)
  46         self.center()
  47         self.path.set_velocity(vx=150, vy=100)
  48 
  49     def walls(self):
  50         vx, vy = self.path.get_velocity()
  51         if not self.onscreen(top=-5, bottom=-5, jail=1):
  52             self.path.set_velocity(vy=-vy)
  53         if not self.onscreen(right=-5, jail=1):
  54             self.path.set_velocity(vx=-vx)
  55 
  56     def hit(self):
  57         vx, vy = self.path.get_velocity()
  58         vx = abs(vx)
  59         self.path.set_velocity(vx=vx)
  60 
  61     def move(self):
  62         self.walls()
  63         Square.move(self)
  64 
  65 
  66 class Pong(Game):
  67     def initialize(self):
  68         self.window.border(left=0, top=5, right=5, bottom=5)
  69         self.window.set_title('Pong!')
  70 
  71         paddle = Paddle()
  72         self.sprites.add(paddle)
  73         ball = Ball()
  74         self.sprites.add(ball)
  75 
  76         self.score = Score()
  77         self.sprites.add(self.score)
  78 
  79         self.events.add(KEYDOWN_Event(key=K_UP, callback=paddle.up))
  80         self.events.add(KEYUP_Event(key=K_UP, callback=paddle.noup))
  81 
  82         self.events.add(KEYDOWN_Event(key=K_DOWN, callback=paddle.down))
  83         self.events.add(KEYUP_Event(key=K_DOWN, callback=paddle.nodown))
  84 
  85         self.paddle = paddle
  86         self.ball = ball
  87 
  88     def checkCollisions(self):
  89         if self.ball.collide(self.paddle):
  90             self.ball.hit()
  91             self.score.addPoints(1)
  92             self.score.updateScore()
  93 
  94         if not self.ball.onscreen(left=10):
  95             self.ball.center()
  96             self.ball.path.set_velocity(vx=150, vy=100)
  97 
  98 
  99 if __name__ == '__main__':
 100     game = Pong()
 101     game.mainloop()