基本上使用tkinter来开发GUI应用需要以下5个步骤:

    1. 导入tkinter模块中我们需要的东西。
    2. 创建一个顶层窗口对象并用它来承载整个GUI应用。
    3. 在顶层窗口对象上添加GUI组件。
    4. 通过代码将这些GUI组件的功能组织起来。
    5. 进入主事件循环(main loop)。

    下面的代码演示了如何使用tkinter做一个简单的GUI应用。

    需要说明的是,GUI应用通常是事件驱动式的,之所以要进入主事件循环就是要监听鼠标、键盘等各种事件的发生并执行对应的代码对事件进行处理,因为事件会持续的发生,所以需要这样的一个循环一直运行着等待下一个事件的发生。另一方面,Tk为控件的摆放提供了三种布局管理器,通过布局管理器可以对控件进行定位,这三种布局管理器分别是:Placer(开发者提供控件的大小和摆放位置)、Packer(自动将控件填充到合适的位置)和Grid(基于网格坐标来摆放控件),此处不进行赘述。

    使用Pygame进行游戏开发

    下面我们来完成一个简单的小游戏,游戏的名字叫“大球吃小球”,当然完成这个游戏并不是重点,学会使用Pygame也不是重点,最重要的我们要在这个过程中体会如何使用前面讲解的面向对象程序设计,学会用这种编程思想去解决现实中的问题。

    制作游戏窗口

    1. def main():
    2. # 初始化导入的pygame中的模块
    3. pygame.init()
    4. # 初始化用于显示的窗口并设置窗口尺寸
    5. screen = pygame.display.set_mode((800, 600))
    6. # 设置当前窗口的标题
    7. pygame.display.set_caption('大球吃小球')
    8. running = True
    9. # 开启一个事件循环处理发生的事件
    10. while running:
    11. # 从消息队列中获取事件并对事件进行处理
    12. for event in pygame.event.get():
    13. if event.type == pygame.QUIT:
    14. running = False
    15. if __name__ == '__main__':
    16. main()

    在窗口中绘图

    可以通过pygame中draw模块的函数在窗口上绘图,可以绘制的图形包括:线条、矩形、多边形、圆、椭圆、圆弧等。需要说明的是,屏幕坐标系是将屏幕左上角设置为坐标原点(0, 0),向右是x轴的正向,向下是y轴的正向,在表示位置或者设置尺寸的时候,我们默认的单位都是。所谓像素就是屏幕上的一个点,你可以用浏览图片的软件试着将一张图片放大若干倍,就可以看到这些点。pygame中表示颜色用的是色光三原色表示法,即通过一个元组或列表来指定颜色的RGB值,每个值都在0~255之间,因为是每种原色都用一个8位(bit)的值来表示,三种颜色相当于一共由24位构成,这也就是常说的“24位颜色表示法”。

    加载图像

    如果需要直接加载图像到窗口上,可以使用pygame中image模块的函数来加载图像,再通过之前获得的窗口对象的blit方法渲染图像,代码如下所示。

    1. import pygame
    2. def main():
    3. # 初始化导入的pygame中的模块
    4. pygame.init()
    5. # 初始化用于显示的窗口并设置窗口尺寸
    6. screen = pygame.display.set_mode((800, 600))
    7. pygame.display.set_caption('大球吃小球')
    8. # 设置窗口的背景色(颜色是由红绿蓝三原色构成的元组)
    9. screen.fill((255, 255, 255))
    10. # 通过指定的文件名加载图像
    11. ball_image = pygame.image.load('./res/ball.png')
    12. # 在窗口上渲染图像
    13. screen.blit(ball_image, (50, 50))
    14. pygame.display.flip()
    15. running = True
    16. # 开启一个事件循环处理发生的事件
    17. while running:
    18. # 从消息队列中获取事件并对事件进行处理
    19. for event in pygame.event.get():
    20. if event.type == pygame.QUIT:
    21. running = False
    22. if __name__ == '__main__':
    23. main()

    实现动画效果

    碰撞检测

    通常一个游戏中会有很多对象出现,而这些对象之间的“碰撞”在所难免,比如炮弹击中了飞机、箱子撞到了地面等。碰撞检测在绝大多数的游戏中都是一个必须得处理的至关重要的问题,pygame的sprite(动画精灵)模块就提供了对碰撞检测的支持,这里我们暂时不介绍sprite模块提供的功能,因为要检测两个小球有没有碰撞其实非常简单,只需要检查球心的距离有没有小于两个球的半径之和。为了制造出更多的小球,我们可以通过对鼠标事件的处理,在点击鼠标的位置创建颜色、大小和移动速度都随机的小球,当然要做到这一点,我们可以把之前学习到的面向对象的知识应用起来。

    1. from enum import Enum, unique
    2. from math import sqrt
    3. from random import randint
    4. import pygame
    5. @unique
    6. class Color(Enum):
    7. """颜色"""
    8. RED = (255, 0, 0)
    9. GREEN = (0, 255, 0)
    10. BLUE = (0, 0, 255)
    11. BLACK = (0, 0, 0)
    12. WHITE = (255, 255, 255)
    13. GRAY = (242, 242, 242)
    14. @staticmethod
    15. def random_color():
    16. """获得随机颜色"""
    17. r = randint(0, 255)
    18. b = randint(0, 255)
    19. return (r, g, b)
    20. class Ball(object):
    21. """球"""
    22. def __init__(self, x, y, radius, sx, sy, color=Color.RED):
    23. """初始化方法"""
    24. self.x = x
    25. self.y = y
    26. self.radius = radius
    27. self.sx = sx
    28. self.sy = sy
    29. self.color = color
    30. self.alive = True
    31. def move(self, screen):
    32. """移动"""
    33. self.x += self.sx
    34. self.y += self.sy
    35. if self.x - self.radius <= 0 or \
    36. self.x + self.radius >= screen.get_width():
    37. self.sx = -self.sx
    38. if self.y - self.radius <= 0 or \
    39. self.y + self.radius >= screen.get_height():
    40. self.sy = -self.sy
    41. def eat(self, other):
    42. """吃其他球"""
    43. if self.alive and other.alive and self != other:
    44. dx, dy = self.x - other.x, self.y - other.y
    45. distance = sqrt(dx ** 2 + dy ** 2)
    46. if distance < self.radius + other.radius \
    47. and self.radius > other.radius:
    48. other.alive = False
    49. self.radius = self.radius + int(other.radius * 0.146)
    50. def draw(self, screen):
    51. """在窗口上绘制球"""
    52. pygame.draw.circle(screen, self.color,
    53. (self.x, self.y), self.radius, 0)

    事件处理

    可以在事件循环中对鼠标事件进行处理,通过事件对象的属性可以判定事件类型,再通过pos属性就可以获得鼠标点击的位置。如果要处理键盘事件也是在这个地方,做法与处理鼠标事件类似。

    上面的两段代码合在一起,我们就完成了“大球吃小球”的游戏(如下图所示),准确的说它算不上一个游戏,但是做一个小游戏的基本知识我们已经通过这个例子告诉大家了,有了这些知识已经可以开始你的小游戏开发之旅了。其实上面的代码中还有很多值得改进的地方,比如刷新窗口以及让球移动起来的代码并不应该放在事件循环中,等学习了多线程的知识后,用一个后台线程来处理这些事可能是更好的选择。如果希望获得更好的用户体验,我们还可以在游戏中加入背景音乐以及在球与球发生碰撞时播放音效,利用pygame的mixer和music模块,我们可以很容易的做到这一点,大家可以自行了解这方面的知识。事实上,想了解更多的关于pygame的知识,最好的教程是,如果英语没毛病就可以赶紧去看看啦。 如果想开发3D游戏,pygame就显得力不从心了,对3D游戏开发如果有兴趣的读者不妨看看。