用Python和Pygame写游戏-从入门到精通(实战三:植物大战僵尸1)

By | 2011/09/27

我们终于把pygame的方方面面都说了一遍,也经过了两个小游戏的洗礼,如果您真的好好学习了每一部分并自动动手演练过,那就是一个很不错的Python游戏开发人员啦!

但是,不得不说我们到现在为止,写的东西都不够酷!pygame就这点能耐么?第一篇介绍pygame的时候,我就说了pygame很适合做一个植物大战僵尸之类的游戏,OK,那么,这次开始我们就来用pygame山寨一个吧哈哈。

pygame版植物大战僵尸

游戏介绍就省略了,不过说实话这游戏我也没玩几关,感受了一下就关了。所以现在仿制的与真品相比肯定差了很多,这里只是稍微把我们学习的东西温习应用一下而已,如果各位看到什么不对劲儿的东西,只要不是很致命,也就一笑而过吧。

创意(抄袭)

游戏是否卖座,创意是极为重要的。所有其他技术性的东西,比如画面音乐都是有可替代性的,也就是说,可以通过学习获得的,而创意则不同,植物大战僵尸之前,有无数的塔防游戏,为什么没有这么火?首先制作精良,上手容易,然后两者的结合给人耳目一新,闯关的模式更是易于上手难于精通,非常具有可挖掘性。当然原因肯定不止这么多,不管是游戏还是电影,成功永远有规律可循,然而却永远无法复制。可以学习一下它的好的地方,但光学习是不可能突破的。

这个虽然极为重要,但不是我们的重点,我们这里基本就是抄袭——哦不,是参考,所以就不多讲了(也没有人能讲出什么东西来)。

资源

好的游戏往往需要极为漂亮的画面,这里也还是发挥拿来主义吧~ 各种背景角色啊音乐都解包复制过来就是了。怎么解包?网上搜一下。

不过如果分析一下资源包里的文件,会发现它分的特别细,比如说一个太阳花,周围一圈花瓣,居然是一个一个分开来存放的!这个引擎还挺有意思。我们的pygame不太适合用这样的图片资源,还是把每一帧完整的分别存放吧。当然可以一帧一个文件,有些麻烦,还增加读盘时间。回想下Surface里学习的东西,我们可以把这么多帧拼成一个图片,然后取一部分就可以了。比如说太阳花,处理以后会是下面的样子:

每个帧都并列排开,太阳花的话一共有18个画面,很长的一张图。每个画面都有共同的高度和宽度,所以一旦我们读进这个文件并转成Surface以后,就拆成18个Subsurface,在渲染的时候,分别画出来,就有动态的效果了。

很显然我们在分割的时候需要知道宽度(高度的话,如果只有一行,就无所谓了),可以在代码里写好,我这里就是这么做的。不过也许自己在写游戏的时候,美工和程序不在一起做,那么就需要良好沟通,或者这种小制作的话,就把图片文件名规定一下,叫【sunflower_82x77_18.png】好了,意思是【名字_宽x高_数.后缀】,这样代码可以根据读入的文件名自动转换。只是举个例子,总之,游戏资源繁多,好好的管理是很重要的。

构架

因为只是做一个demo,太复杂的构架也就免了,这次我就随便写写了。而且也没有什么绝对优秀的构架可以放到哪里都好用,根据要求自己选择判断才是重要的。

开始——太阳花摆起来

我们之前的各种动画,变化的只有位置,在一款优秀的游戏中,这个是不允许存在的,飞机什么的可能还好说一些,要是有人物走动的时候,脚也不动一下岂不见鬼了?不过,难道每画一帧就换一个图片贴上去么?好像很麻烦啊……

放心,一点都不比移动困难,温习一下之前的Sprite篇(其实AI部分结束那个时候我就开始穿插写实战了,发现缺少Sprite的知识实在不方便,就加了一个Sprite的介绍,实战就挪到了最后),我们既然把画图全部交给Sprite来做,那么图片的切换也可以交给它来做。

MAIN_DIR = os.path.split(os.path.abspath(__file__))[0]

def load_image(file, width=None, number=None):
    file = os.path.join(MAIN_DIR, 'data/image', file)
    try:
        surface = pygame.image.load(file).convert_alpha()
    except pygame.error:
        raise SystemExit('Could not load image "%s" %s'%(file, pygame.get_error()))
    if width == None:
        return surface
    height = surface.get_height()

    return [surface.subsurface(
        Rect((i * width, 0), (width, height))
        ) for i in xrange(number)]

class SunFlower(pygame.sprite.Sprite):
    _width = 82
    _height = 77
    _number = 18
    images = []
    def __init__(self):
        self.order = 0
        pygame.sprite.Sprite.__init__(self)
        if len(self.images) == 0:
            self.images = load_image("sunflower.png", self._width, self._number)
        self.image = self.images[self.order]
        self.rect = Rect(0, 0, self._width, self._height)

    def update(self):
        if self.order >= self._number - 1:
            self.order = -1
        self.order += 1
        self.image = self.images[self.order]

这里有两个内容,一个是load_image函数,它接受三个参数,图片文件名、每一帧的宽度和总帧数,它所做的事情就是把图片读入之后分割Subsurface,把一个Subsurface的列表返回。这个函数被SunFlower类的初始化函数调用,返回值存放在静态变量images里。

为什么把Subsurface列表存为静态变量,也是有含义的。这样一来这个图片只会第一次新建SunFlower类的实例的时候加载一次,下一次在新建一个太阳花的时候,就不会再读取sunflower.png文件了,节约内存和时间。

然后看update方法,这里做的事情便是是SunFlower当前的image指向下一个Subsurface(到头的话重新开始),这样每次blit的时候,就会和上一次有些差别,动态效果就出来了。

不过对于“基于时间的移动”那部分学习的比较好的话,很肯会考虑到一个问题。我们这里是一帧切换一个画面,万一游戏变得很复杂或者运行的机器很糟糕,每一帧都花了大量时间的话,岂不是就动的很慢了?牛机器上的话,又要摆的像螺旋桨一样看着就要飞起来似地……怎么办?一样,使用基于时间的帧切换

class SunFlower(pygame.sprite.Sprite):
    _rate = 100
    _width = 82
    _height = 77
    _number = 18
    images = []
    def __init__(self):
        self.order = 0
        pygame.sprite.Sprite.__init__(self)
        if len(self.images) == 0:
            self.images = load_image("sunflower.png", self._width, self._number)
        self.image = self.images[self.order]
        self.rect = Rect(0, 0, self._width, self._height)
        self.life = self._life
        self.passed_time = 0

    def update(self, passed_time):
        self.passed_time += passed_time
        self.order = ( self.passed_time / self._rate ) % self._number
        if self.order == 0 and self.passed_time > self._rate:
            self.passed_time = 0
        self.image = self.images[self.order]

与上一个SunFlower类相比,我们多了一个_rate静态变量,然后初始化的时候也多了一个passed_time静态变量,update也多了一个参数。尽管看起来有变得复杂了一些,这一切都是值得的!_rate意味着一帧画面要保持多少毫秒,passed_time则记录着游戏经过了多少毫秒(为了避免passed_time无限增大,我在合适的时候把它归零了),我们通过这两个参数计算出现在应该显示第几帧,尽管形式不同,这和基于时间的移动是一个道理。希望大家能看明白(不明白的话多看几遍……再看不明白的去复习第8部分,再不明白的,面壁去……哦,我是说咱俩都得去面壁)。

这里不就先不给出完整的程序了,相信有了前面的经验很容易就可以写一个框子运行起来,自己试一下?如果你能看到类似左边的画面,你就赢了!准备迎接下一波惊喜吧。

如果没能成功的运行起来(虽然似乎有点不应该),也没关系,这个系列完成以后会放出完整代码的。

本次使用的太阳花资源图片: sunflower.png

37 thoughts on “用Python和Pygame写游戏-从入门到精通(实战三:植物大战僵尸1)

  1. iCyOMiK

    专门关了ABP想去点你那坑爹的广告,但看到“看妇科,治不孕”,实在点不下手。。。

    Reply
  2. xishui Post author

    @iCyOMiK: 哈哈哈,要是众人皆如此君,何愁天下不平?

    Reply
  3. Ken

    楼主把内容再丰富一下,再细致一点,可以出本小册子卖钱了。您这文笔,这内容,绝对行。

    Reply
  4. Liu_Comix

    哈哈~还记得我么?去年写蛋疼俄罗斯方块的快乐小2B~写完俄罗斯方块就不再编程了,那些代码也随着某次系统崩溃而一去不返了。昨天稀里糊涂竟然又点到了大神的博客!竟然还在更新啊~真是了不起!佩服的同时也不禁对自己的半途而废而感到羞愧……

    今天的问题是……SunFlower类中update函数的参数passed_time具体代表什么?我直接用pygame.time.get_ticks()当做passed_time了,然后那小太阳花,摇的很“诡异”……求指点迷津啊~

    另外,植物大战僵尸系列啥时候再更新啊?

    Reply
    1. freeman

      个人认为
      self.passed_time += passed_time
      应该改成
      self.passed_time = passed_time

      Reply
  5. Liu_Comix

    OK了~原来前面的博文有写过啊~哈哈,我的小太阳花儿终于可以正常“摇头”了~

    Reply
  6. 安问

    _life没有定义和使用,稍微调整了一下,可以了~不知博主此坑是否会填,先多去实战一下了~

    Reply
  7. xumaomao

    小问题:第一段代码中第9行还是用is None 比较好一点。
    此外我不太明白的一点是第一段代码中SunFlower类里的images,为什么它是静态变量?不太明白是怎么实现静态的,搜了一下见到一些staticmethod,self.__class__.之类的东西,看不懂了。

    Reply
  8. klose

    从第一节课追到最后了,对博主产生了强烈的崇拜之情~可能有生之年不会更新了,剩下的靠我们自己吧~

    Reply
  9. LT

    从第一节课追到最后了,对博主产生了强烈的崇拜之情~可能有生之年不会更新了,剩下的靠我们自己吧~+1

    Reply
  10. 张持良

    十分佩服!你收我当小第,我绝对服气

    Reply
  11. 多情剑客

    博主这一系列教程写得太好了!
    非常感谢!

    Reply
  12. P2P理财问答

    国民党前主席连战和夫人连方瑀结縭逾50载,相知相惜情比金坚,他知道老婆醉心诗词爱唱歌,去年金婚宴上送老婆录的唱片当伴手礼,前晚更包下松菸诚品表演厅,一圆老婆 理财知识大全 开演唱会的梦想。连家往来无白丁,300多位政要名流受邀出席,问到夫人开唱心情如何,连战笑答:「相当兴奋!」果然「连奶奶」準备充分,73岁的她唱满23首歌,加上3首安可曲,全程不需大字报,让人刮目相看。?据悉,连奶奶当初唱诗词是为了让孙子能够背熟经典,连战也很得意儿孙唐诗倒背如流,前晚他上台致词时,观众怂恿连战「亲她一下」,他先是笑笑说「唉呀,我们都七老八十了」,这时夫妻竟然异口同声说「这种事不稀奇了」,逗得大家哄堂大笑,之后连战主动说「亲一

    Reply
  13. 投微盘

      基金投资技巧  大家的周末过得还愉快吗?今天周一啦,我们就来听听新鲜事吧。小编今天给大家带来的是刚出炉的全国排行榜,什么排行榜呢?那就是2016中国财政透明度排行榜,不过据说只有两个省份”及格”,你们的家乡排第几呢?  11月26日,上海财经大学公共政策研究中心在上海发布《2016年中国财政透明度报告》,在对31个省(自治区、直辖市)财政信息展开调查后,研究计算得出中国财政透明度平均分数为42.25分(总分100分)。31个省份中超过60分只有两个省份,分别是宁夏和湖南。江苏省财政透明度分数垫底,为23.71分。   根据这份排行榜显示,今年仅有两个省份宁夏、湖南及格,换而言之超过97%都没有达标,其中排

    Reply
  14. Max

    博主为什么不继续了呢, 已经四年了。是觉得pygame 不行了吗

    Reply
      1. 粥盟

        17年更新了!
        Downloads
        Not sure what to download? Read the Installation Notes.
        1.9.3 Packages (January 16th 2017)
        Source
        pygame-1.9.3.tar.gz ~ 2MWheel packages are also available on PyPI,and may be installed by running pip install wheel
        1.9.1 Packages (August 6th 2009)
        Source
        pygame-1.9.1release.tar.gz ~ 1.4M – source/docs/examples in unix format
        pygame-1.9.1release.zip ~ 1.5M – source/docs/examples in windows format

        Reply
      2. 粥盟

        17年更新了!
        Downloads
        Not sure what to download? Read the Installation Notes.
        1.9.3 Packages (January 16th 2017)
        Source
        pygame-1.9.3.tar.gz ~ 2MWheel packages are also available on PyPI,and may be installed by running pip install wheel
        1.9.1 Packages (August 6th 2009)
        Source
        pygame-1.9.1release.tar.gz ~ 1.4M – source/docs/examples in unix format
        pygame-1.9.1release.zip ~ 1.5M – source/docs/examples in windows format

        Reply
  15. 香芋糕

    请问楼主 如何将资源包的细小文件 合起来的啊?我想继续做下去,解完包了,不会用Ps合吧:(

    Reply
  16. circle orbit

    我在SunFlower初始化的时候把私有变量_rate公有化,并且取值搞了个random,最后出来的flower摆动频率都不一样的样子很有喜感,嘿嘿

    Reply
  17. aaa

    楼主有生之年应该不会再更新了……

    Reply
  18. 带猎犬狩猎狗

    博主的文章做得很好,思路很清晰;但对新学者来说,代码里的各种函数让人头疼,不熟悉的话就可能花很多时间去查找,这里推荐一篇总结性文章‘’https://www.cnblogs.com/aland-1415/p/8736680.html‘’,pygame里的函数基本都有而且有注释,希望后面的朋友能学得更轻松~~。 快十年了~博主已经忘了吧

    Reply
  19. Pingback: 用 Python 和 Pygame 写游戏 – 从入门到精通(目录) – ITPCB

P2P理财问答进行回复 取消回复

您的电子邮箱地址不会被公开。