如何pygame python3.5开发库开发python游戏

Pygame是跨平台Python模块,专为电子游戏设计。
这是Youtube上点击率最高的Pygame视频教程。
本视频教程总共分了100个课时讲解Pygame。详见视频。
相信大家都知道英语对于程序员的重要性,通过学习本教程又能提高英语,何乐而不为。
本站已将教程上传到百度网盘:
密码:zfll
需要字幕的同学建议在线观看:
(观看前请先确保打开访问,否则网站访问失败)
本文固定链接:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:339051次
积分:6840
积分:6840
排名:第2511名
原创:359篇
转载:55篇
评论:64条
难度:初级
类型:技术教程
难度:初级
类型:技术教程
难度:初级
类型:技术教程
文章:14篇
阅读:43085
文章:72篇
阅读:110538
(11)(11)(15)(35)(26)(29)(42)(7)(3)(1)(1)(4)(2)(1)(1)(6)(2)(9)(26)(30)(77)(10)(4)(6)(7)(16)(37)京 东 价:
[定价:¥]
PLUS会员专享价
支  持:
搭配赠品:
所 在 地:江苏 南京市
服务支持:
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
Python和Pygame游戏开发指南+MINECRAFT我的世界 新手完全攻略 MC编程
加载中,请稍候...
商品介绍加载中...
扫一扫,精彩好书免费看
服务承诺:
京东平台卖家销售并发货的商品,由平台卖家提供发票和相应的售后服务。请您放心购买!
注:因厂家会在没有任何提前通知的情况下更改产品包装、产地或者一些附件,本司不能确保客户收到的货物与商城图片、产地、附件说明完全一致。只能确保为原厂正货!并且保证与当时市场上同样主流新品一致。若本商城没有及时更新,请大家谅解!
权利声明:京东上的所有商品信息、客户评价、商品咨询、网友讨论等内容,是京东重要的经营资源,未经许可,禁止非法转载使用。
注:本站商品信息均来自于合作方,其真实性、准确性和合法性由信息拥有者(合作方)负责。本站不提供任何保证,并不承担任何法律责任。
印刷版次不同,印刷时间和版次以实物为准。
价格说明:
京东价:京东价为商品的销售价,是您最终决定是否购买商品的依据。
划线价:商品展示的划横线价格为参考价,该价格可能是品牌专柜标价、商品吊牌价或由品牌供应商提供的正品零售价(如厂商指导价、建议零售价等)或该商品在京东平台上曾经展示过的销售价;由于地区、时间的差异性和市场行情波动,品牌专柜标价、商品吊牌价等可能会与您购物时展示的不一致,该价格仅供您参考。
折扣:如无特殊说明,折扣指销售商在原价、或划线价(如品牌专柜标价、商品吊牌价、厂商指导价、厂商建议零售价)等某一价格基础上计算出的优惠比例或优惠金额;如有疑问,您可在购买前联系销售商进行咨询。
异常问题:商品促销信息以商品详情页“促销”栏中的信息为准;商品的具体售价以订单结算页价格为准;如您发现活动商品售价或促销信息有异常,建议购买前先联系销售商咨询。
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
浏览了该商品的用户还浏览了
加载中,请稍候...
iframe(src='///ns.html?id=GTM-T947SH', height='0', width='0', style='display: visibility:')从零开发一个小游戏:PyGame入门
从零开发一个小游戏:PyGame入门
编程派微信号:codingpy编程派坚信,这个时代人人都学点编程,不论对自身成长还是日常工作,都将有巨大益处。因此,选择了通过本公号与大家分享最易学的编程语言 Python 的教程和资源,希望对你有帮助。&今天分享的是&&最新译文,原文来自real python,是一篇比较详细的 PyGame 游戏开发入门指南。译者:haiyuqiao,华中科技大学(在读研究生),正在使用 Python 做数据分析。Fighting from now!校对:EarlGrey,编程派主页君。以下是正文,一起来学习吧。下文中下划线文字,可阅读原文后点击访问。另外,本文代码略有删减,完整代码可阅读原文。是&&库的 Python 包装器(wrapper)。SDL 是一个跨平台库,支持访问计算机多媒体硬件(声音、视频、输入等)。SDL 非常强大,但美中不足的是,它是基于 C 语言的,而 C 语言比较难懂,因此我们采用 PyGame 。在本教程中,我们将介绍 PyGame 的基本逻辑和冲突检测,以及如何在屏幕上绘图和将外部文件导入到游戏中。提示:教程假定你对 Python 的语法、文件结构和面向对象的程序设计已经有了基本的了解。准备工作打开,根据你的操作系统和 Python 版本下载合适的 PyGame 安装包。如果你使用的是 Python 3,那么请下载.EarlGrey:在下载页面找不到 1.9.2 版的下载链接,但是&pip install&可以安装。新建一个&.py&文件,然后输入以下代码:import pygamefrom pygame.locals import *
pygame.init()与其他 Python 程序一样,我们首先导入想要使用的模块。这里,我们将导入&pygame&和&pygame.locals&,后续我们将使用其中的一些常量。最后一行会初始化所有导入的 PyGame 模块,在做其他操作之前必须执行调用该函数。基础对象屏幕对象首先,我们需要一张画布,我们称之为“屏幕”,它是我们绘画的平台。为了创建一个屏幕,我们需要调用pygame.display&中的&set_mode&方法,然后向&set_mode()&传递包含屏幕窗口宽度和高度的元组(本教程中使用 800x600 尺寸)。screen = pygame.display.set_mode((800,600))运行上述代码,将会弹出一个窗口,然后当程序退出后又立即消失。一点都不酷嘛,对吧?下一节,我们将介绍游戏的主循环,它将确保只有在我们给它正确的输入时程序才会退出。游戏主循环是所有操作发生的地方。在游戏过程中,它不断的更新游戏状态,渲染游戏画面和收集输入指令。创建循环时,需要确保我们有办法跳出循环,退出应用。为此,我们将同时介绍一些基本的用户输入指令。所有的用户输入(和我们稍稍后提到的其他事件)都会进入 PyGame 的事件队列,通过调用&pygame.event.get()&可以访问该队列。这将返回一个包含队列里所有事件的列表,我们将循环这个列表,并根针对相应的事件类型做出反应。现在我们只关心&KEYDOWN&和&QUIT&事件:# 用于保证主循环运行的变量runnning = True# 主循环!while running: & &# for 循环遍历事件队列
& &for event in pygame.event.get(): & & & & & &# 检测 KEYDOWN 事件: KEYDOWN 是 pygame.locals 中定义的常量,pygame.locals文件开始已经导入
& & & &if event.type == KEYDOWN: & & & & & &# 如果按下 Esc 那么主循环终止
& & & & & &if event.key == K_ESCAPE:
& & & & & & & &running = False
& & & & # 检测 QUIT : 如果 QUIT, 终止主循环
& & & &elif event.type == QUIT:
& & & & & &running = False将上述代码添加到之前的代码下,并运行。你应该看到一个空的窗口。只有你按下 ESC 键 或者触发一个 QUIT 事,否则这个窗口不会消失。Surface 和 RectsSurface和&Rects是 PyGame 中的基本构件。可以将 Surface 看作一张白纸,你可以在上面随意绘画。我们的屏幕对象也是一个 Surface 。它们可以包含图片。Rects 是 Surface 中矩形区域的表示。让我们创建一个 50x50 像素的 Surface,然后给它涂色。由于屏幕是黑色的,所以我们使用白色。 我们然后调用&get_rect()&在 Surface上 得到一个矩形区域和 Surface 的 x 轴 和 y 轴。# 创建Surface 并用原则设定它的长度和宽度surf = pygame.Surface((50,50))# 设定Surface的颜色,使其和屏幕分离surf.fill((255,255,255))
rect = surf.get_rect()Blit 和 Flip仅仅只是创建了 Surface 并不能在屏幕上看到它。为此我们需要将这个 Surface 绘制()到另一个 Surface 上。Blit 是一个专业术语,意思就是绘图。你仅仅只能从一个Surface Blit 到另一个Surface,我们的屏幕就是一个 Surface 对象。以下是我们如何将&surf&画到屏幕上:# 这一行表示:将surf画到屏幕 x:400.y:300的坐标上screen.blit(surf,(400,300))
pygame.display.flip()blit()&有两个参数:要画的 Surface 和 在源 Surface 上的坐标。此处我们使用屏幕的中心,但是当你运行代码时,你会发现我们的&surf&并没有出现在屏幕的中心。这是因为&blit()&是从左上角开始画 surf 。注意在 blit 之后的&pygame.display.filp()&的调用。将会更新自上次 flip 后的整个屏幕,两次 flip 之间发生的修改都将在屏幕上显示。没有调用flip()那就什么也不会出现。Sprites什么是 Sprites ?从编程术语来讲,Sprites 是屏幕上事物的二维表达。本质上来讲,Sprite 就是一个图片。Pygame 提供一个叫做 Sprites 的基础类,它就是用来扩展的,可以包含想要在屏幕上呈现的对象一个或多个图形表示。我们将会扩展类,这样可以使用它的内建方法。我们称这个新的对象为&Player&。Plyaer&将扩展 Sprite,现在只有两个属性:surf&和&rect。我们也会给&surf&涂色(本教程使用白色),如之前 surface 例子,只是现在 Surface 属于 Player :class Player(pygame.sprite.Sprite): & &def __init__(self):
& & & &super(Player,self).__init__()
& & & &self.surf = pygame.Surface((75,25))
& & & &self.surf.fill((255,255,255))
& & & &self.rect = self.surf.get_rect()现在我们将上述代码整合在一起:EarlGrey:此处代码省略。运行上述代码,你将会在屏幕中心看到一个白色的矩形:如果将&screen.blit(player.surf,(400,300))&改成&screen.blit(player.surf,player.rect)&,你觉得会发生什么?修改之后,试着在控制台中打印&player.rect&。rect&的前两个属性分别是&rect&左上角的 x 和 y 轴坐标。当你将 rect 传递给 blit ,它将会根据这个坐标画 surface 。我们后续将使用它控制 player 移动。用户输入现在开始才是有趣的部分。我们要把 Player 变得可控制!之前我们提过,按键事件&pygame.event.get()&将把最新的事件从事件堆(event stack)中移除。Pygame 还有另外一个,pygame.event.get_pressed()。get_pressed()方法返回一个队列,其中包含了所有按键事件组成的字典,我们将把它放在主循环中,这样我们将在每一帧上的按键。pressed_keys = pygame.event.get_presssed()现在我们将写一个方法,接收上面那个字典,并且根据按下的键定义 sprite 的行为,代码如下:def updata(self,pressed_keys):
& &if pressed_keys[K_UP]:
& & & &self.rect.move_ip(0,-5) & &if pressed_keys[K_DOWN]:
& & & &self.rect.move_ip(0,5) & &if pressed_keys[K_LEFT]:
& & & &self.rect.move_ip(-5,0) & & &if pressed_keys[K_RIGHT]:
& & & &self.rect.move_ip(5,0)K_UP、K_DOWN、K_LEFT、K_RIGHT&对应键盘上的上、下、左 右方向键。我们判断这些键是否按下,如果它为真,那么我们就朝相应的方向移动&rect()。Rects 有两个内建的移动方法,此处我们使用&&move_ip()&,因为我们希望移动 rect 并且不用复制它。将上述方法添加到&Player&类,将&get_pressed()&调用放在主循环中。整体代码现在应该是这样的:EarlGrey:此处代码省略。现在你可以使用方向键移动矩阵块了。也许你注意到了,你可以将矩形块移出屏幕,这可能并不是你想要的。所以我们我们需要往 update 方法中添加一些逻辑,检测矩形的坐标是否移出了 800x600 的屏幕边界;如果出了边界,那么就将它放回在边界上:def updata(self,pressed_keys):
& &if pressed_keys[K_UP]:
& & & &self.rect.move_ip(0,-5) & &if pressed_keys[K_DOWN]:
& & & &self.rect.move_ip(0,5) & &if pressed_keys[K_LEFT]:
& & & &self.rect.move_ip(-5,0) & &if pressed_keys[K_RIGHT]:
& & & &self.rect.move_ip(5,0) & &# 限定player在屏幕中
& &if self.rect.left & 0:
& & & &self.rect.left = 0
& &elif self.rect.right & 800:
& & & &self.rect.right = 800
& &if self.rect.top &= 0:
& & & &self.rect.top = 0
& &elif self.rect.bottom &= 600:
& & & &self.rect.bottom = 600上述代码没有使用&move&方法,我们只要修改 上下左右的相应坐标即可。现在我们添加一些敌人!首先我们创建一个新的 sprite 类,命名为&Enemy。依照创建 player 的格式创建:class Enemy(pygame.sprite.Sprite): & &def __init__(self):
& & & &super(Enemy, self).__init__()
& & & &self.surf = pygame.Surface((20, 10))
& & & &self.surf.fill((255, 255, 255))
& & & &self.rect = self.surf.get_rect(center=(820, random.randint &(0, 600)))
& & & &self.speed = random.randint(5, 20) & &def update(self):
& & & &self.rect.move_ip(-self.speed, 0) & & & &if self.rect.right & 0:
& & & & & &self.kill()以上有几点需要说明。首先,当我们在 surface 上调用&get_rect&时,我们将核心属性 x 设为 820 ,y 的坐标为一个随机数,由&random.randint()&生成。在最终的代码中,我们会在文件的开头导入&&库(import random)。为什么选择随机数?因为我们希望敌人从屏幕右边(820)的随机位置(0-600)上出现。 我们还将使用&random&设置敌人的速度属性,这样敌人就会有快有慢。敌人的&update()&方法没有参数限制(我们不关心敌人的输入),只要让它向着屏幕左边以一定的速度移动就可以了。update 方法中的最后一个&if&语句检测敌人右侧是否通过了屏幕左边边界(要确保它们不会一碰到屏幕的边界就消失)。当他们通过屏幕的边界后,我们调用 Sprite 的内建方法&kill()&,从 sprite 组中删除它们,这样它们就不会再被渲染出来。kill 不会释放被它们占用的内存, 需要你确保你不再引用它们,以便 Python 的垃圾回收器回收。GroupsPygame 提供的另一个很有用的对象是 Sprite 的&。诚如其名,是 Sprite 的集合。为什么我们要使用 sprite.Group 而不是列表呢? 因为 sprite.Group 有一些内建的方法,有助于解决冲突和更新问题。那现在就创建一个 Group,用来包含游戏中的所有 Sprites 。创建完 Group 后,我们要将 Player 添加到里面,因为它是我们目前唯一的 Sprite 。我们也可以为敌人创建一个 group 。 当我们调用 Sprite 的&kill()&方法时,sprite 将会从其所在的全部 group 中删除。enemies = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)现在有了&all_sprites&的 group ,我们接着改变对象渲染方式,只要渲染 group 中的所有对象即可:for entity in all_sprites:
& &screen.blit(entity.surf,entity.rect)现在,任何放到&all_sprites&中的对象都会被渲染出来。自定义事件现在我们为敌人创建了一个 sprite.Group ,但是并没有实际的敌人。那怎样才能在屏幕上出现敌人呢?我们当然可以在刚开始的时候创建一堆的敌人,但是这样游戏玩不了几秒。为此,我们创建一个自定义事件,它隔几秒钟就会触发创建一批敌人。我们要监听该事件,方式和监听按键或退出事件一样。创建自定义事件十分容易,只要命名即可:ADDENEMY = pygame.USEREVENT +1这样就可以了!现在,我们有了一个叫做&ADDENEMY的事件,可以在主程序中监听它。这里我们只需要注意一点,即自定义事件需要有一个独特的值,要比&USEREVENT&的值大,这就是我们为什么设定它为&USEREVENT + 1。这里说明一点:自定义事件本质上就是整数常量。又因为比&USEREVENT&小的数值已经被内置函数占据,所以创建的任何自定义事件都要比&USEREVENT大。定义好事件之后,我们需要将它插入事件队列中。因为整个游戏过程中都要创建它们,所以将设置一个计时器。可以通过 PyGame 的&time()&对象实现。pygame.time.set_timer(ADDENEMY,250)这行代码告诉 PyGame 每隔 250 毫秒(四分之一秒) 触发一次&ADDENEMY&事件。这是在主游戏循环之外执行的,不过在整个游戏中都处于执行状态。现在我们添加一些监听事件的代码:while running: & &for event in pygame.event.get(): & & & &if event.type == KAYDOWN: & & & & & &if event.key == K_ESCAPE:
& & & & & & & &running == False
& & & &elif event.type == QUIT:
& & & & & &running == False
& & & &elif (event.type == ADDENEMY):
& & & & & &new_enemy = Enemy()
& & & & & &enemies.add(new_enemy)
& & & & & &all_sprites.add(new_enemy)谨记:set_timer()&只能用来将事件插入到 PyGame 事件队列中,不做其他任何事情。现在我们会监听ADDENEMY事件,当它触发时,将创建一个&Enemy类的实例。然后我们将实例添加到enemies&这个 Sprite Group(后续用它来检测冲突)和&all_sprites&Group(这样它会和其他对象一起渲染)。冲突这才是 PyGame 的魅力所在!写冲突代码(collision code)很难,但是 PyGame 提供了很多冲突检测方法,你可以在查看其中一部分。本次教程使用&。spritecollideany()&接受一个 Sprite 对象和一个 Sprite.Group ,检测 Sprite 对象是否和 Sprite Group 中的其他 Sprites 冲突。这样,我们可以拿 Player 和敌人所在的 Sprite Group 对比,检测 player 是否被敌人击中。代码实现如下:if pygame.sprite.spritecollideany(player,enemies):
& &player.kill()检测 player 是否&enemies&中的 Sprites 冲突,如果发生冲突了,那么调用&player&Sprite 的&kill()&方法。因为我们只渲染了&all_sprites&Group 中的 sprites ,kill()&方法将从其所在的全部 Groups 中移出 Sprite ,player就不再出现,算是“杀死它了”。目前的完整代码如下:EarlGrey:此处代码省略。测试一下!图片现在游戏可以玩了,但是长得挺丑的。接下来,我们将白色方块变成有意思的图片,让游戏看上去有游戏的样子。前面的代码示例中,我们使用了涂色的 Surface 对象表示游戏里的所有事物。虽然这样有助于理解什么是 Surface 和它如何工作,但是却让游戏变得很丑!现在我们要给 player 和 enemy 添加一些图片。我喜欢自己画图,我把 player 画成小飞机,enemy 是导弹,这些可以从中下载。欢迎你使用我的作品,自己画或者者下载一些。修改对象的构造函数下面是我们现在的 player 构造函数:class &Player(pygame.sprite.Sprite): & &def __init__(self):
& & & &super(Player,self).__init__()
& & & &self.surf = pygame.Surface((75,25))
& & & &self.surf.fill((255,255,255))
& & & &self.rect = self.surf.get_rect()新的构造函数将会是这个样子的:class &Player(pygame.sprite.Sprite): & &def __init__(self):
& & & &super(Player,self).__init__()
& & & &self.image = pygame.image.load('jet.png').convert()
& & & &self.image.set_colorkey((255,255,255),RLEACCEL)
& & & &self.rect = self.image.get_rect()我们想用一张图片替代 Surface 对象。我们将使用&pygame.image.load()&导入图片的路径。load()&方法将会返回一个 Surface 对象。我们然后在这个 Surface 对象上调用&convert()&创建副本,这样可以更快地将它画在屏幕上。接下来,我们在图片上调用&set_colorkey()&方法。set_colorkey用于设置图片的颜色,如果不设置 Pygame 会将图片设置为透明。这里我选用白色,因为和飞机的背景色一致。&是一个可选参数,它有助于 PyGame 在非加速显示器上更快地渲染。最后,我们和之前一样调用&rect()&对象:在图片上调用&get_rect()。谨记:图片仍然是一个 surface 对象,只不过它上面画了一张图。对 enemy 构造函数做同样的操作:class Enemy(pygame.sprite.Sprite): & def __init__(self):
& & & &super(Enemy, self).__init__()
& & & &self.image = pygame.image.load('missile.png').convert()
& & & &self.image.set_colorkey((255, 255, 255), RLEACCEL)
& & & &self.rect = self.image.get_rect(
& & & & & &center=(random.randint(820, 900), random.randint(0, 600))
& & & &self.speed = random.randint(5,20)现在的游戏虽然和以前一样,但是比之前漂亮多啦!但是我仍然觉得它少了点什么东西。让我们加点不断漂浮的白云,这样会有飞机划过蓝天的感觉。为此,我们需要遵循之前用过的一些原则。首先,我们创建&cloud&对象,画上白云的照片,其&update()&方法让它不停地向着屏幕左边移动。然后,我们需要添加一个自定义事件,每隔一段时间就生成白云(我们还要将添加白云到&all_spritesgroup)。白云对象的实现如下:class Cloud (pygame.sprite.Sprite): & &def __init__(self):
& & & &self.image = pygame.image.load('cloud.png').convet()
& & & &self.image.set_colorkey((0,0,0),RLEACCEL)
& & & &self.rect = self.image.get_rect(
& & & & & &center = (random.randint(820,900),random.randint(0,600))
& & & &) & &def update(self):
& & & &self.rect.move_ip(-5,0) & & & &if self.rect.right & 0:
& & & & & &self.kill()上面的代码看起来都很熟悉,下面这个事件创建代码也一样,我们将它放在 enemy 事件代码下面:ADDCLOUD = pygame.USEREVENT+2pygame.time.set_timer(ADDCLOUD,1000)现在为它们创建一个新的 Sprite.Group:clouds = pygame.sprite.Group()现在,在主循环中,我们需要开始监听ADDCLOUD事件。下面的代码:for event in pygame.event.get(): & &if event.type == KEYDOWN: & & & &if event.kay == K_ESCAPE:
& & & & & &running = False
& &elif event.type == QUIT:
& & & &running = False
& &elif event.type == ADDENEMY:
& & & &new_enemy = Enemy()
& & & &enemies.add(new_enemy)
& & & &all_sprites.add(new_enemy)将会变成这样:for event in pygame.event.get(): & &if event.type == KEYDOWN: & & & &if event.kay == K_ESCAPE:
& & & & & &running = False
& &elif event.type == QUIT:
& & & &running = False
& &elif event.type == ADDENEMY:
& & & &new_enemy = Enemy()
& & & &enemies.add(new_enemy)
& & & &all_sprites.add(new_enemy) &
& &elif event.type == ADDCLOUD:
& & & &new_cloud = Cloud()
& & & &all_sprites.add(new_cloud)
& & & &clouds.add(new_cloud)我们还要把 clouds 添加到&all_sprites&Group 和新的 clouds Group 中。我们这么做,是因为我们使用&all_sprites&来渲染,使用&clouds&调用它们的 update 函数。也许你很好奇,为什么我们将它不添加到&enemies&Group中;毕竟我们调用的是一样的 update 函数。原因在于,我们不想检测白云和飞机之间的冲突。我们的飞机要顺畅地通过所有的云层。现在剩下的就是调用 clouds Group 的&update()&方法了!结语大功告成!让我们测试一下,效果应该是这样的:完整代码可以在获取!希望本教程对你有用!是EarlGrey@编程派发起成立的一个专注于 Python 技术内容翻译的小组,目前已有近 30 名 Python 技术爱好者加入。翻译组出品的内容(包括教程、文档、书籍、视频)将在编程派微信公众号首发,欢迎各位 Python 爱好者推荐相关线索。推荐线索,直接在编程派微信公众号推文下留言即可。
发表评论:
TA的推荐TA的最新馆藏[转]&&Python_pygame游...
扫描二维码,下载文件到手机
当前文件信息
浏览:74次
下载:25次
您的VIP会员已过期,是否续费?
用户应遵守著作权法,尊重著作权人合法权益,不违法上传、存储并分享他人作品。举报邮箱:
京网文[0号 京ICP证100780号:转载时请以超链接形式标明文章原始出处和作者信息及本声明
申明:原文链接:作者主页:本文已征得原文作者同意,由Arthur1989翻译为中文,你可以任意转载本文,请注明出处,尤其是原英文出处,谢谢
这是的第1部分.
游戏是编程中应用性最强的领域之一.即使是编写最为简单的游戏,你也不得不考虑图形,数学,物理甚至于人工智能等知识.它是一项很有趣的练习编程的方法.
如果你是Python的一名粉丝(就算你不是),而且你又对游戏的设计很感兴趣,那么对你而言将是一个很伟大的模块,它专为电子游戏设计,你绝对不容错过.它能在大部分平台上运行,并且提供了许多简洁的工具,以管理复杂图形世界中的移动和声音.
因特网上有大量的Pygame的教程,但绝大部分是相对基础的.即便是这本书也限于介绍的层次.为了达到精通的境界,我决定自己写一个教程,希望它能为那些想要使用Pygame的人提供进一步的台阶.
本教程强烈建议你自己动手修改代码,不妨做做课后练习.这对于理解你所学到的知识很有帮助.
正如我前面提到过的,本教程并不适合毫无经验的新手.如果你刚刚开始学习Pygame,你可以暂时移步到.简单介绍了Pyagme,它也是值得参考的.
这里,我假设一了解下面的知识:
&&Python(你可以不是高级开发者,但绝不能是完全的初学者)&&基本的数学和物理知识(容器,矩形,移动法则,概率,等等).我会介绍并不常见的技巧,但是我不会教你如何向容器添加元素等.&&熟悉Pygame.也就是,你至少看过上面提过的一个教程.
哦,还有一件事&本教程主要集中于2D游戏.3D游戏完全是一个新的层次,比起粗糙的3D模型,我更喜欢相对简单但是完整的游戏.
让我们开始吧.
在这一部分中,我们最终会实现这么一个模型&一个creeps(译注: 野生生物,下文中此词不作翻译.后面有对该词的介绍)的完全模拟器,圆形的生物在屏幕上四处爬行,时不时地碰到墙而改变方向.
尽管这还不是一个游戏,但它确实一个有用的起点,我们可以从它出发实现各色各样的想法.目前为止,我很享受于慢慢决定最终它会变成什么游戏.
本教程第1部分的完整代码可以在,它包含所有需要的图片.我建议你下载它,并且运行里面的演示程序.手头有程序的代码是很有帮助的.我的测试环境是Python 2.5.2和Pygame 1.8.1,但它应该能够在其他版本上运行.
Pygame的文档
Pygame的写的很不错.它列出了Pyagme中的所有模块,类,常量以及函数,对于每一个你并不熟悉的类/模块,我建议你多多参考这个资源.
好吧,首先让我们看看本教程第1部分的目标.
&&我们希望creeps可以在屏幕上四处爬行.&&creeps的数量和形状是可以很容易的配置的.&&creeps碰到墙之后将会真实的反弹.&&为了让程序更加有趣,creeps的移动将是随机的.
那么,究竟creep是什么呢?
一个creep就是一张可以旋转(通过Pygame)并且移动位置的小图片.让旋转了的图片足够好看是一件充满艺术的事,这超出了我的能力,因此我限定图片的旋转角度必须是45的倍数.(意味着creep可以往东南西北和东北,西北,东南,西南这8个方向移动)
在中,creep的图片是.png格式的文件[1]
注意,所有的creep图片都有相同的方向.后面我们将会知道,这是很重要的一点.
creep是怎么移动的呢?
正如你已经毫无疑问的读过的Pygame的简单教程中提到的一样(你一定读过了吧?),物体的移动仅仅是一种视觉的幻象.在显示屏上,其实没有什么东西真的是在移动.相反的,为了让人产生物体移动的错觉,游戏仅仅是飞快地显示了一系列的图片,这些图片之间相差无几.对大众来说,每秒更新频率超过30的一组图片看起来已经足够流畅了.
为了实现屏幕的周期刷新,在游戏代码中我们使用&game loop&(循环).
游戏的循环
就像所有的GUI程序一样,每个游戏都有它自己的&主循环&.在Pygame中,你可以用一个Python循环来实现它,这是很简单的.下面是我们的主循环:
# The main game loop
while True:
# Limit frame speed to 50 FPS
time_passed = clock.tick(50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit_game()
# Redraw the background
screen.fill(BG_COLOR)
# Update and redraw all creeps
for creep in creeps:
creep.update(time_passed)
creep.blitme()
pygame.display.flip()
让我们看看程序,怎么样?好吧,让我们看看程序里到底发生了什么.正如我说过的,它是你Python循环的基础&不会终止,除非用户打算退出.你可以看得出来,pygame.QUIT是唯一的事件处理函数,当用户点击程序的关闭框时,它将被激活.
这个循环多久运行一次呢?这是由对clock的调用决定的.clock是pygame.time.Clock的一个对象,它在上面代码之前已被创建.对clock的调用大概是这样子的:程序休眠直到下一个1/50秒(上界).在实际中,设定游戏的刷新率为50FPS(帧每秒)是很不错的,因为一方面,我们希望游戏看起来流畅;另一方面,我们并不希望游戏消耗太多的CPU资源.你可以尝试设置不同的刷新率来看看效果.比如说,将它设置为10PFS时,演示程序看起来怎么样?而且,你可以看看练习题1和练习题3.
顺便说一下,现在,你自己在文档中查阅的介绍的机会来了.
真正有趣的事情接下来就要发生了.在每一次循环中,屏幕都将被背景色重绘,并且所有的creep状态都将被更新,然后显示在屏幕上.最后,显示(译注:原文是display,这是Pygame的一个重量级的概念,为清晰起见,下文中display将被翻译为&显示(display)&)的重绘是通过flip来完成的(是的,现在你应该查看了).
在主循环之前发生了什么
现在,让我们看看主循环之前发生了什么吧:
# Game parameters
SCREEN_WIDTH, SCREEN_HEIGHT = 400, 400
BG_COLOR = 150, 150, 80
CREEP_FILENAMES = [
'bluecreep.png',
'pinkcreep.png',
'graycreep.png']
N_CREEPS = 20
pygame.init()
screen = pygame.display.set_mode(
(SCREEN_WIDTH, SCREEN_HEIGHT), 0, 32)
clock = pygame.time.Clock()
# Create N_CREEPS random creeps.
creeps = []
for i in range(N_CREEPS):
creeps.append(Creep(screen,
choice(CREEP_FILENAMES),
randint(0, SCREEN_WIDTH),
randint(0, SCREEN_HEIGHT)),
choice([-1, 1]),
choice([-1, 1])),
好吧,并没有什么魔法.前面几行代码的意思是自明的.假设你已经懂得如何初始化Pygame,如何创建一个显示(display)对象.那么,creep的创建是怎样的呢?
creeps是一串Creep对象的链表&这正是creep图片的核心和灵魂.下面是Creep类的声明,它包含了类Creep的构造函数:
class Creep(Sprite):
""" A creep sprite that bounces off walls and changes its
direction from time to time.
def __init__(
self, screen, img_filename, init_position,
init_direction, speed):
""" Create a new Creep.
The screen on which the creep lives (must be a
pygame Surface object, such as pygame.display)
img_filaneme:
Image file for the creep.
init_position:
A vec2d or a pair specifying the initial position
of the creep on the screen.
init_direction:
A vec2d or a pair specifying the initial direction
of the creep. Must have an angle that is a
multiple of 45 degres.
Creep speed, in pixels/millisecond (px/ms)
构造函数的参数是组织得很好的,你可以看到,当一个Creep的对象creep被创建时,实参是如何被传递给形参的:
creeps.append(Creep(screen,
choice(CREEP_FILENAMES),
randint(0, SCREEN_WIDTH),
randint(0, SCREEN_HEIGHT)),
choice([-1, 1]),
choice([-1, 1])),
首先,我们把屏幕的图层(译注:原文是surface,为另一Pygame的概念,下文译为&图层(surface)&)传递给Creep.Creep用它来计算碰墙后如何反弹(译注:即,通过图层(surface)的边界知道墙的坐标),还有,在哪里显示自己.
接下来,Creep随机地从图片列表中选出一张作为自己的图片(choice是Python的标准random库中的一个函数),而且,它被随机地设置了它在屏幕上的初始位置(randint也是random中的函数)),初始方向(后文再作讨论).((译注,这句话中的&屏幕&,根据作者的意思是,客户区).速度的单位是0.1 px/ms(像素每毫秒),或者说,每秒100像素.
向量和方向
在creeps演示程序中,这一部分也许最为简单了.在游戏编程中,对向量的融会贯通很重要,因为在涉及到屏幕上图像的运动时,向量是主要的数学工具.
我们使用向量来完成两件事.一件是描述位置和速度(位移).你肯定知道的,在XY坐标轴上的一个点的位置可以用一个2维向量表示.位置和速度这两个向量的区别在于,位移是有矢量的.换句话说,在原来的位置向量上加上一个位移向量,得到的是另一个位置的向量:
这是很不错的,但还是有一点点的别扭.在数学里,正如上图所示,XY坐标系是这样子的,X轴正方向向右,Y轴正方向向上.但是,在屏幕上绘图时,事情有点不一样.在大部分的图形绘制接口中,客户区的左上角逻辑坐标为(0,0),X轴正方向向右,Y轴正方向向下.也就是说,图形绘制中,XY坐标系是这样子的:
上图很重要!它表示了我们在creep演示程序中将要使用到的8个&.这些也就是creep能够前进的方向了(都是45度的倍数).在阅读下文之前,请确定你已经理解了这一点.
还记得Creep构造函数中的方向参数吗?它用来设置creep的初始方向.实际上,构造函数允许接受一个pair(译注,一种二维的数据结构),它将被转为向量并且规范化.(比如说,传进(-1,-1)将得到期望的西北方向)
当creep决定改变方向或者撞到墙壁时,creep的方向将发生改变.
很意外地,在Pygame发布的版本中,并没有带上一个&标准的&向量的实现.因此游戏开发者要么自己写一个,要么上网找一个向量的模块.
在下载链接的包中,含有一个vec2d.py文件,它是一个2维向量是实现,我是在上找到它的.该文件很漂亮地实现了2维向量的许多有用的功能.你不必读懂它的所有代码,但你可以看一看练习题4.
更新creep的信息
这个演示程序中最有趣的部分是Creep类中的Update函数.
def update(self, time_passed):
这个函数由主循环调用,它接受的参数是自从上次调用之后过去的时间(以毫秒计).通过这一点,我们就能计算creep的下一个位置.
让我们一步步的剖析update函数的代码:
# Maybe it's time to change the direction ?
self._change_direction(time_passed)
# Make the creep point in the correct direction.
# Since our direction vector is in screen coordinates
# (i.e. right bottom is 1, 1), and rotate() rotates
# counter-clockwise, the angle must be inverted to
# work correctly.
self.image = pygame.transform.rotate(
self.base_image, -self.direction.angle)
(Arthur1989注:未完,待续.)
首先,当creep随机的改变方向时,内联函数_change_direction将被调用.在读懂update函数之后,回过头来你会觉_change_direction其实非常简单,所以我把它留在了练习题5.
update的下一操作是将creep的图像往正确的方向旋转一定的角度.还记得每个creep图片是怎么指向正确方向的吗?这对于不断正确更新creep方向而言来说相当重要.transform.rotate(读它的文档!)根据所得到的角度将给定的图层进行逆时针旋转.
现在解释一下,为什么我们传递给transform.rotate函数的角度是负角度呢?其实这正是因为上文中我介绍的Y轴翻转了的&XY坐标系&.想想一下creep的基图像:(顺便提一下,它在Creep的构造函数中被加载.)
假设我们想得到的是,在我们视觉中(即,物理坐标系),creep朝东南方向的图像.如果我们将45度传递给rotate,那么我们得到的将是creep朝东北方向的图像(因为rotate函数做的是逆时针旋转).因此,为了正确的旋转creep,我们必须将旋转角度取反.
接下来,在update函数中,我们看到:
# Compute and apply the displacement to the position
# vector. The displacement is a vector, having the angle
# of self.direction (which is normalized to not affect
# the magnitude of the displacement)
displacement = vec2d(
self.direction.x * self.speed * time_passed,
self.direction.y * self.speed * time_passed)
self.pos += displacement
正如我说过的,self.direction是一个规范化的向量,它代表creep运行的方向.规划化是很重要的,因为我们不希望它影响位移的正确计算复杂度.位移的计算是简单的在x轴和y轴两个方向上的将速度乘以时间.
Update函数中其余的代码处理creep碰墙反弹的情形.为了让它更为智能,我打算首先介绍creep是如何绘制到屏幕上的.
&是游戏编程者间通用的传递图片(或模式)到可绘图层的行话.在Pygame中,这是通过函数实现的:
def blitme(self):
""" Blit the creep onto the screen that was provided in
the constructor.
# The creep image is placed at self.pos.
# To allow for smooth movement even when the creep rotates
# and the image size changes, its placement is always
# centered.
draw_pos = self.image.get_rect().move(
self.pos.x - self.image_w / 2,
self.pos.y - self.image_h / 2)
self.screen.blit(self.image, draw_pos)
位块传送,就像中其他很多功能一样,使用功能多面的pygame.Rect类.blit函数接收的参数为:一张图片(实际上是一个图层),一个矩形,它用来指定blit调用的图层,图片将绘制到该图层上.
当然,我们必须提供creep当前所在的坐标,在该坐标上绘制creep.但是,我们还需要对坐标做一些小小的调整,为什么呢?
这是因为,当图片在Pygame中旋转时,它的面积会增大,下面是解释:
因为图片是矩形的,因此在旋转之后的图片中,Pygame必须保持原有图片的所有信息,因此,旋转后的图片面积可能会增大.这种情况只发生在旋转角度不是90度的倍数的时候,你可以做做练习题6.
所以,不管什么时候creep改变了方向,它的面积都会变大(译注:相对于上一时刻方向未变时的面积).如果不做一定的调整,在creep每次改变方向的那一刻,它的位置会发生位移,这将使得演示程序看起来不够流畅顺滑.
其实所谓的调整是很简单的:每次要绘制creep时,我们将其绘制在它的中点(重心)位置.请看代码:
draw_pos = self.image.get_rect().move(
self.pos.x - self.image_w / 2,
self.pos.y - self.image_h / 2)
self.screen.blit(self.image, draw_pos)
上面计算所得到的正是creep图像的重心位置.即便是creep旋转时,面积变大了,它的重心还是不会改变的.
首先,请确定上面的&重心&的技巧你已经掌握了(参考练习题7).如果你确实已经掌握了,那么creep碰墙反弹是很容易的,下面是这部分功能的代码:
# When the image is rotated, its size is changed.
# We must take the size into account for detecting
# collisions with the walls.
self.image_w, self.image_h = self.image.get_size()
bounds_rect = self.screen.get_rect().inflate(
-self.image_w, -self.image_h)
if self.pos.x & bounds_rect.left:
self.pos.x = bounds_rect.left
self.direction.x *= -1
elif self.pos.x & bounds_rect.right:
self.pos.x = bounds_rect.right
self.direction.x *= -1
elif self.pos.y & bounds_rect.top:
self.pos.y = bounds_rect.top
self.direction.y *= -1
elif self.pos.y & bounds_rect.bottom:
self.pos.y = bounds_rect.bottom
self.direction.y *= -1
首先,通过将与屏幕重叠的矩形转为图层计算屏幕的边界(长和宽).(为了使用重心代表creep所在位置,这是必须的.)
然后,对于四面墙,我们计算creep是不是撞上它们了,如果是的话,在理想的情况下,creep将与墙成镜面反射弹出.让我们对其中一扇墙做分析:
if self.pos.x & bounds_rect.left:
self.pos.x = bounds_rect.left
self.direction.x *= -1
这部分代码是计算creep与左边的墙碰撞后的方向的.creep总是从右方撞上左墙,因此,只要将creep的方向向量中X取反,Y保持不变,creep就会以正确的方向反弹了.
到这里,我们已经看过creeps.py中大部分最有趣的代码了.如果你有什么不懂的地方,不妨对照着本教程中的图片再一次仔细地查看完整的代码.如果还是有什么不明白的话,请告诉我,我会很乐于帮助你的.
相同的教程/幻灯片,不同的人理解的层次不同.最基础的是读懂它,高级一点的是实践它.为了真正地掌握知识,你必须勇于挑战教程中没有解释过的那一部分.因此,我再一次建议你看看下面的练习题,想想到底该怎么做.最好你能写出答案,并且在代码上实现出来.
接下来是什么?
这个creep的演示程序对于一些游戏来说,是个不错的模板.我还没有决定接下来我要写些什么,我也还没决定究竟要把教程写到什么地步.因此,我接下来的打算将取决于读者的反馈.请随意留言或者发我邮件.
1.通过修改N_CREEPS常量的值来增加creep的数量.在我的桌面电脑上,在有上百哥creep时,演示程序还是运行得很流畅.2.修改生成creep的代码块,使得所有creep中有60%是灰色的,20%是蓝色和粉色的.3.调用主循环中的clock.tick函数.尝试打印出相邻的两次tick间经过了多长时间,然后修改tick的参数.如果你不提供任何参数,tick函数将尽可能快地运行.计算一下,当creep数目增加时,相邻的两次tick间时间是多少.4.打开vec2d.py文件,读懂它的所有函数和属性.5.修改_change_direction函数,来改变creep的行为.
(1)让它们更频繁地改变方向.
(2)你能让creep每隔一段时间停止不动1秒钟吗?(提示:修改速度函数)
6.你能用基本的三角几何知识计算出在Pygame中,图片旋转45度,它的面积增大多少吗?7重写绘制函数,删去图像的重心位置作为绘制位置的代码(直接使用未调整的图像的位置).演示程序运行起来怎么样?
历史上的今天:
引用地址:
访问统计:

我要回帖

更多关于 pygame for python3.5 的文章

 

随机推荐