用Python和Pygame写游戏-从入门到精通(2)

By | 2011/04/14

上次我们试着写了一个最简单的Pygame程序并且解释了一个大概的框架,这次就Pygame中也是游戏中最关键(……好吧,也许并不是关键,但绝对是至关重要的一项)的事件来展开。

此图为一个用Pygame开发的游戏,或许有些简陋,但是只要你有爱,什么都能出来!

理解事件

事件是什么,其实从名称来看我们就能想到些什么,而且你所想到的基本就是事件的真正意思了。我们上一个程序,会一直运行下去,直到你关闭窗口而产生了一个QUIT事件,Pygame会接受用户的各种操作(比如按键盘,移动鼠标等)产生事件。事件随时可能发生,而且量也可能会很大,Pygame的做法是把一系列的事件存放一个队列里,逐个的处理。

事件检索

上个程序中,使用了pygame.event.get()来处理所有的事件,这好像打开大门让所有的人进入。如果我们使用pygame.event.wait(),Pygame就会等到发生一个事件才继续下去,就好像你在门的猫眼上盯着外面一样,来一个放一个……一般游戏中不太实用,因为游戏往往是需要动态运作的;而另外一个方法pygame.event.poll()就好一些,一旦调用,它会根据现在的情形返回一个真实的事件,或者一个“什么都没有”。下表是一个常用事件集:

事件 产生途径 参数
QUIT 用户按下关闭按钮 none
ATIVEEVENT Pygame被激活或者隐藏 gain, state
KEYDOWN 键盘被按下 unicode, key, mod
KEYUP 键盘被放开 key, mod
MOUSEMOTION 鼠标移动 pos, rel, buttons
MOUSEBUTTONDOWN 鼠标按下 pos, button
MOUSEBUTTONUP 鼠标放开 pos, button
JOYAXISMOTION 游戏手柄(Joystick or pad)移动 joy, axis, value
JOYBALLMOTION 游戏球(Joy ball)?移动 joy, axis, value
JOYHATMOTION 游戏手柄(Joystick)?移动 joy, axis, value
JOYBUTTONDOWN 游戏手柄按下 joy, button
JOYBUTTONUP 游戏手柄放开 joy, button
VIDEORESIZE Pygame窗口缩放 size, w, h
VIDEOEXPOSE Pygame窗口部分公开(expose)? none
USEREVENT 触发了一个用户事件 code

如果你想把这个表现在就背下来,当然我不会阻止你,但实在不是个好主意,在实际的使用中,自然而然的就会记住。我们先来写一个可以把所有方法输出的程序,它的结果是这样的。

我们这里使用了wait(),因为这个程序在有事件发生的时候动弹就可以了。还用了font模块来显示文字(后面会讲的),下面是源代码:

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
SCREEN_SIZE = (640, 480)
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)

font = pygame.font.SysFont("arial", 16);
font_height = font.get_linesize()
event_text = []

while True:

    event = pygame.event.wait()
    event_text.append(str(event))
    #获得时间的名称
    event_text = event_text[-SCREEN_SIZE[1]/font_height:]
    #这个切片操作保证了event_text里面只保留一个屏幕的文字

    if event.type == QUIT:
        exit()

    screen.fill((255, 255, 255))

    y = SCREEN_SIZE[1]-font_height
    #找一个合适的起笔位置,最下面开始但是要留一行的空
    for text in reversed(event_text):
        screen.blit( font.render(text, True, (0, 0, 0)), (0, y) )
        #以后会讲
        y-=font_height
        #把笔提一行

    pygame.display.update()

小贴士:
书上说,如果你把填充色的(0, 0, 0)改为(0, 255, 0),效果会想黑客帝国的字幕雨一样,我得说,实际试一下并不太像……不过以后你完全可以写一个以假乱真甚至更酷的!

这个程序在你移动鼠标的时候产生了海量的信息,让我们知道了Pygame是多么的繁忙……我们第一个程序那样是调用pygame.mouse.get_pos()来得到当前鼠标的位置,而现在利用事件可以直接获得!

处理鼠标事件

MOUSEMOTION事件会在鼠标动作的时候发生,它有三个参数:

  • buttons – 一个含有三个数字的元组,三个值分别代表左键、中键和右键,1就是按下了。
  • pos – 就是位置了……
  • rel – 代表了现在距离上次产生鼠标事件时的距离

和MOUSEMOTION类似的,我们还有MOUSEBUTTONDOWNMOUSEBUTTONUP两个事件,看名字就明白是什么意思了。很多时候,你只需要知道鼠标点下就可以了,那就可以不用上面那个比较强大(也比较复杂)的事件了。它们的参数为:

  • button – 看清楚少了个s,这个值代表了哪个按键被操作
  • pos – 和上面一样

处理键盘事件

键盘和游戏手柄的事件比较类似,为KEYDOWNKEYUP,下面有一个例子来演示使用方向键移动一些东西。

background_image_filename = 'sushiplate.jpg'

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()

x, y = 0, 0
move_x, move_y = 0, 0

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
           exit()
        if event.type == KEYDOWN:
            #键盘有按下?
            if event.key == K_LEFT:
                #按下的是左方向键的话,把x坐标减一
                move_x = -1
            elif event.key == K_RIGHT:
                #右方向键则加一
                move_x = 1
            elif event.key == K_UP:
                #类似了
                move_y = -1
            elif event.key == K_DOWN:
                move_y = 1
        elif event.type == KEYUP:
            #如果用户放开了键盘,图就不要动了
            move_x = 0
            move_y = 0

        #计算出新的坐标
        x+= move_x
        y+= move_y

        screen.fill((0,0,0))
        screen.blit(background, (x,y))
        #在新的位置上画图
        pygame.display.update()

当我们运行这个程序的时候,按下方向键就可以把背景图移动,但是等等!为什么我只能按一下动一下啊……太不好试了吧?!用脚掌考虑下就应该按着就一直动下去才是啊!?Pygame这么垃圾么……

哦,真是抱歉上面的代码有点小bug,但是真的很小,你都不需要更改代码本身,只要改一下缩进就可以了,你可以发现么?Python本身是缩进编排来表现层次,有些时候可能会出现一点小麻烦,要我们自己注意才可以。

KEYDOWN和KEYUP的参数描述如下:

  • key – 按下或者放开的键值,是一个数字,估计地球上很少有人可以记住,所以Pygame中你可以使用K_xxx来表示,比如字母a就是K_a,还有K_SPACEK_RETURN等。
  • mod – 包含了组合键信息,如果mod & KMOD_CTRL是真的话,表示用户同时按下了Ctrl键。类似的还有KMOD_SHIFTKMOD_ALT
  • unicode – 代表了按下键的Unicode值,这个有点不好理解,真正说清楚又太麻烦,游戏中也不太常用,说明暂时省略,什么时候需要再讲吧。

事件过滤

并不是所有的事件都需要处理的,就好像不是所有登门造访的人都是我们欢迎的一样。比如,俄罗斯方块就无视你的鼠标,而在游戏场景切换的时候,你按什么都是徒劳的。我们应该有一个方法来过滤掉一些我们不感兴趣的事件(当然我们可以不处理这些没兴趣的事件,但最好的方法还是让它们根本不进入我们的事件队列,就好像在门上贴着“XXX免进”一样),我们使用pygame.event.set_blocked(事件名)来完成。如果有好多事件需要过滤,可以传递一个列表,比如pygame.event.set_blocked([KEYDOWN, KEYUP]),如果你设置参数None,那么所有的事件有被打开了。与之相对的,我们使用pygame.event.set_allowed()来设定允许的事件。

产生事件

通常玩家做什么,Pygame就产生对应的事件就可以了,不过有的时候我们需要模拟出一些事件来,比如录像回放的时候,我们就要把用户的操作再现一遍。

为了产生事件,必须先造一个出来,然后再传递它:

my_event = pygame.event.Event(KEYDOWN, key=K_SPACE, mod=0, unicode=u' ')
#你也可以像下面这样写,看起来比较清晰(但字变多了……)
my_event = pygame.event.Event(KEYDOWN, {"key":K_SPACE, "mod":0, "unicode":u' '})
pygame.event.post(my_event)

你甚至可以产生一个完全自定义的全新事件,有些高级的话题,暂时不详细说,仅用代码演示一下:

CATONKEYBOARD = USEREVENT+1
my_event = pygame.event.Event(CATONKEYBOARD, message="Bad cat!")
pgame.event.post(my_event)

#然后获得它
for event in pygame.event.get():
    if event.type == CATONKEYBOARD:
        print event.message

这次的内容很多,又很重要,一遍看下来云里雾里或者看的时候明白看完了全忘了什么的估计很多,慢慢学习吧~~多看看动手写写,其实都很简单。

下次讲解显示的部分。

>> 用Python和Pygame写游戏-从入门到精通(3)

153 thoughts on “用Python和Pygame写游戏-从入门到精通(2)

    1. lusong

      #!/usr/bin/env python
      #coding=utf-8

      import pygame
      from pygame.locals import *
      from sys import exit

      background_image_filename = ‘sushiplate.jpg’
      pygame.init()
      screen = pygame.display.set_mode((640, 480), 0, 32)
      background = pygame.image.load(background_image_filename).convert()

      x, y = 0,0
      move_x, move_y = 0, 0

      while True:
      for event in pygame.event.get():
      if event.type == QUIT:
      exit()
      if event.type == KEYDOWN:
      print(event.key)
      if event.key == K_LEFT:
      move_x = -1
      elif event.key == K_RIGHT:
      move_x = 1
      elif event.key == K_UP:
      move_y = -1
      elif event.key == K_DOWN:
      move_y = 1
      elif event.type == KEYUP:
      move_x = 0
      move_y = 0

      x += move_x
      y += move_y

      screen.fill((0, 0 ,0))
      screen.blit(background, (x,y))
      pygame.display.update()

      这种格式可以分析出结果,在keydown和keyup处打印键值,可以看到按一次按键产生一个键值,即使不松开按键,也只会产生一个键值,所以按住不松开是一个事件。按照上面写法。一直按住上键,会产生一个键值,走完一次while循环,在第二次while循环时,走到for循环会卡住(原来作者的写法。因为没有新事件产生),修改后会跳过for循环,执行位移操作。松开后产生新的时间(keyup事件),while循环获取事件,修改偏移量为零,停止位移。

      Reply
  1. 77

    同意上面,后5行往左缩进两格。
    要传递最终结果,而不是每次结果

    Reply
  2. 77

    你好,在处理键盘事件的例子中,for event in pygame.event.get():语句何种情况下退出此循环?
    后5行缩进了后,应该是执行完for再执行这后5句,为何当我按下右键感觉for的语句和后五句是同时执行的呢?难道是event机制?不懂啊

    Reply
  3. 77

    @77: 再补充一下,按住右键不放,是一个事件还是多个啊。。。

    Reply
    1. liyiyan

      用它给的第一个程序测试可知,按住不放是1个事件

      Reply
  4. 77

    @77: 在for循环中,print event 发现按住右键,是多个事件。。。

    Reply
  5. 77

    抱歉打扰了,一个事件的话那我就懂了。while是为了让一个事件产生的结果,不断地影响后面的语句。当松开按钮就给值为0,并不断地继续影响后面的结果。。。

    Reply
  6. xishui Post author

    @77: 自问自答的可爱77,congratulation!很多时候,把问题前后调查一下,答案也就呼之欲出了。

    Reply
    1. 初学者

      能教我这个愚笨的人怎样在windows7上安装livewires模块吗?我点了里面的setup.py一点用也没,大师请教教

      Reply
      1. water

        setup.py 不是点击的啊。。。。。。。你百度怎么安装setup.py。。。

        Reply
    2. balabala

      我运行作者最一开始写的代码,如果一直摁住一个键也能移动,只不过移动得慢。所以,我能理解为,如果后面5行代码如果if同级(就是在执行完if之后再执行后面5行代码),相比与for同级(if与后5行代码同时执行),运行得慢吗?

      Reply
  7. wazedix

    试了下这个上下左右移动,如果按键交换太快,就会导致错乱失效,而且不支持同时按下几个方向键,于是我改进了下:

    x, y = 320, 240
    move = {K_LEFT:0, K_RIGHT:0, K_UP:0, K_DOWN:0}
    
    
    while True:    
        for event in pygame.event.get():
            if event.type == QUIT:
               exit()
            if event.type == KEYDOWN:
                if event.key in move:
                    move[event.key] = 1
            elif event.type == KEYUP:
                if event.key in move:
                    move[event.key] = 0
        x -= move[K_LEFT]
        x += move[K_RIGHT]
        y -= move[K_UP]
        y += move[K_DOWN]
        screen.fill((0,0,0))
        screen.blit(picture, (x,y))
        pygame.display.update()
    Reply
    1. wayhome

      赞!不过有个小瑕疵:同时松开两个键时图片会往其中一个方向移动一像素。始终不知道为什么!

      Reply
      1. GYLheihei

        不可能是同时的,总有一个先放,这时后放的移动

        Reply
    2. 珏七

      赞 不过我好像遇到了一个问题 ,按下键移动以后,图像动的太快了 ,解决这个是需要控制帧数吗?

      Reply
        1. joy

          帧数最低好像不能再低了,太快了可以试试在每次的循环反馈中加入time.sleep()。

          Reply
    3. 1

      你的理解高啊,就是先统计好上下左右都动多少,然后统一的一动一次就到最后,所以可以识别多个按钮一起按

      Reply
    4. 橙みこ

      完全没有遇到这个问题,而且是支持同时按下几个方向键的。

      Reply
  8. xishui Post author

    @wazedix: Great! 我帮您把空格加回去了,HTML总会把多个空格合并为一个,除非把代码放在<pre>…</pre>里面。

    Reply
  9. wei

    为什么x+= move_x
    y+= move_y放在for循环里是移动一次啊?不是一直循环吗?

    Reply
  10. wei

    这个我也理解了,原来是本来没理解透event.get()…

    Reply
  11. xishui Post author

    @wei: 每当想帅气的回答疑问的时候,却突然发现你们自己搞明白了,就会在欣慰感叹之余又有了一些莫名的落寞感……

    Reply
  12. rabbitfan

    大神老师啊,我在定义一个新的函数
    def gameover(root,screen):
    root.close()
    screen.exit()

    这样的话 ,执行这个函数时,pygame的窗口“screen”会关闭么?如果不行,该怎么办呢?

    Reply
  13. Yo

    为何啊 还是不能理解 为什么只能移动一下 get不是提取多个event么

    Reply
  14. Yo

    @Yo:
    哈哈哈理解了 原来一直按着键算一个事件么
    之前以为一直按着键算一直都是那个事件

    Reply
  15. KidSc

    @wazedix:
    同时按住左右键或者上下键,你的方法似乎更好一些~~

    Reply
  16. fatfatface

    我有个小问题
    第二个程式
    按着上下左右其中一颗键后
    移动滑鼠
    图也会跟着动,为什么会这样?

    Reply
    1. wayhome

      虽然不知道你能不能看到,但我还是想说一说:
      我也遇到了这个情况,也解决了,原来我把最外if 语句的elif部分忽略了,不知你是否是这种情况。这个疏忽会导致另一个问题,不知你有没有发现,当key up的时候背景也会同方向移动一个像素。我猜想光标移动一个像素点同key_up 都是一种最。。。基本的事件,不知道说的准确不,但它们确实导致同一种现象,真正原理我却不知道。

      Reply
      1. wayhome

        我知道了:如果if语句不以elif结束,pygame.event.get()接收到key_up(我们松键),或者任何事件(如光标移动一个像素),x和y都会再运算一次。

        Reply
  17. MaximusZhou

    @wazedix:,@xishui: 这样做,还是有一点问题,同时按下左右键,然后再按向上方向的键,图并不会向上移,但若同时按下左右键,然后再按向下方向的键,则图会向下移,请问这是怎么回事

    Reply
  18. 1111

    Traceback (most recent call last):
    File “C:Documents and Settings小电脑桌面用Python和Pygame写游戏-从入门到精通2moveImg.py”, line 17, in
    exit()
    SystemExit

    在按下关闭按钮的时候总是报这个错误
    并且卡死,最后窗口是强制关闭的,我想问问是不是在python2.7.2里退出函数有修改?

    Reply
  19. 月符

    @1111: 调用exit()先调用pygame.exit()就不会卡死了。SystemExit是正常现象,exit()就是通过SystemExit异常终止解释器进程的,文档上有说

    Reply
    1. Geegle

      同意,第一个例子也是这样,关闭按钮时就死了,不知道为什么,然后看到别人有用 pygame.quit() 时就试了一下,

      Reply
  20. Mr Li

    我复制你写的源码来执行
    >>> while True:
    event=pygame.event.wait()
    event_text.append(str(event))
    event_text=event_text[-SCREEN_SIZE[1]/font_height:]
    if event.type==QUIT:
    exit()
    screen.fill((255,255,255))
    y=SCREEN_SIZE[1]-font_height
    for text in reversed(event_text):
    screen.blit(font.render(text,True,(0,0,0)),(0,y))
    y-=font_height
    pygame.display.update()

    我执行后IDLE报出:
    Traceback (most recent call last):
    File “”, line 4, in
    event_text=event_text[-SCREEN_SIZE[1]/font_height:]
    TypeError: slice indices must be integers or None or have an __index__ method
    >>>

    我用windows命令提示符也报这个错,我用的是python 3.2.2,请帮忙查一下错在哪里。谢谢!

    Reply
    1. xishui Post author

      请使用2.X的Python来执行。。。

      Reply
      1. 幽浮

        如果已经是3.3了,那应该怎样改呢?

        Reply
    2. heloo

      event_text是个LIST,LIST后的【】里要是整型,所以,在【】里加个int()进行转化就行了

      Reply
        1. cncser

          把切片event_text[-SCREEN_SIZE[1]/font_height:]中的“/”用“//”替换即可。

          Reply
          1. Chris.Y

            我也遇到了同样的问题,感谢你的回答,但是为什么会出现这样的情况呢?是python版本的问题吗?我是python新手,刚入门就要学习pygame。。

      1. 张伟

        同样是这个问题, bug是这样解决. 原来是python3和2版本问题

        Reply
        1. 张伟

          / 在python2里是整数除法, 结果是整数, 在python3里 / 结果是浮点数, python3里 // 才是整数除法. 这个地方意外巩固了这个知识点的基础

          Reply
    3. 刘**

      event_text = event_text[-SCREEN_SIZE[1]*font_height:]
      list[-x:]代表切片操作从倒数第x个元素,到最后。
      我刚运行的时候也是出现这个问题

      Reply
    4. onlooker

      改成这样就好了event_text=event_text[int(-SCREEN_SIZE[1]/font_height):]

      Reply
  21. cfy

    我现在才开始学习,不知道编者是否还在,请多多指教呀

    Reply
  22. hcl

    学渣的路过,有木有具体的关于这些method的用法,里面有哪些可以调用,怎么调用,有什么作用,我目前都不清楚。理解初学者,求具体教程。

    Reply
  23. xumaomao

    我感觉把最后5行缩进之后,可以做按下方向键进行多次移动的操作,但这种操作貌似与运行程序的电脑速度有关:如果机子速度快,while循环进行的很密集,在按下按键的那段时间里得到的key_down events很多,移动的也就多,反之亦然。

    Reply
  24. 康慨

    我觉得那个if event.type == QUIT那里,在sys.exit()之前还要添加pygame.quit(),否则在我的电脑上每次运行都报错…

    Reply
  25. fromjupiter

    第二段例程琢磨了许久,大概了解了,有些问题:
    1,似乎event.get()中只读取事件,不会消除已读取的事件?那有什么method可以实现消除已读取事件吗?
    2,同时按(左/右)和(上/下)可以实现斜向移动,但是为什么同时按上下或者同时按左右 不会像想象中的一样固定不动?
    我的想法是: 事件中同时存在UP和DOWN,那for循环结束后move_y应该为0啊?
    请指教。
    3,如果想实现那种“长按方向键时,移动速度逐渐加快”,应该怎么做?我的想法是还要设个计时器来记录时间距离,不知道event对象里有发生时间这个属性么?

    Reply
    1. 张伟

      用if, 而不要用elif就能同时按左右而不动了. 我也是程序设计逻辑问题, 把学习进度拖住了,好慢啊!

      Reply
  26. scx0237

    不知道哪缩进,好像怎么按都是event
    出发一次就执行一次呀~~~
    自己好笨 求解答

    Reply
    1. 张伟

      好像博主这篇重在写语法学习, 不在代码业务逻辑, 解释的确实少了. 不过这个也只有靠自己想才有编程趣味, 属于编程思想. 但是另一方面, 初学者光学语言了, 没时间想算法, 有些慢的

      Reply
  27. scx0237

    pygame.key.set_repeat(200, 2)
    加到第十行

    Reply
  28. 叶子

    @Mr Li 把-SCREEN_SIZE[1]/font_height:改成-SCREEN_SIZE[1]//font_height:就可以了
    请问,为什么改了缩进后就可以一直移动了呢,按下方向键不是依旧是一个事件吗,难道和for有关系吗?

    Reply
  29. lxr1010@gmail.com

    @Mr Li: 在2.X里/得到的是int,在3.X里边是除法,需要来个int()

    Reply
  30. wanghuixd

    我运行出现错误: python2.7 pygame1.9 win32

    Traceback (most recent call last):
    File “E:/Python27/pygame_example/game3.py”, line 20, in
    print event.event_name(KEYDOWN)
    AttributeError: event member not defined

    Reply
  31. Pingback: pygame系列_游戏中的事件 - Python教程 - 开发者

  32. cobolMao

    评论的貌似有些晚。呵呵,11年接触的PYTHON,至今一直QQ未改名字,目前在银行工作,用的COBOL。
    但依然没有减退我对PYTHON的热爱,特地最近学习这个语言。谢谢您热心的分享,对于后来之人,这些是您成功的宝贵经验与成果。再次谢谢!

    Reply
  33. Pingback: pygame系列_游戏中的事件 - Python教程 - 开发者

  34. GYLheihei

    我觉得按下移动 有时候太快了,可以加个延时函数 不知道行不
    while (True):
    time+=1
    if not time%10000 == 0:
    continue

    Reply
    1. 张伟

      pygame.time.Clock()对象的tick()方法限制FPS就是这样的原理, 即在while循环下加时间暂停, 否则是按cpu速度刷新屏幕的, 自然太快. 不过你这小段代码我看不懂, 还有while后面的True还能放在括号里面, 我也是第一次才学到 实践了一下 没有报错

      Reply
  35. GYLheihei

    我试了一下 的确是wazedix的程序号 = =

    Reply
  36. shawn

    event_text = event_text[-SCREEN_SIZE[1]/font_height:]
    这句话要改成
    event_text = event_text[int(-SCREEN_SIZE[1]/font_height):] 才行。。不然类型不对。无法运行。。

    Reply
  37. happy球

    video system not initialized是什么情况啊?

    Reply
  38. Mr.chen

    为什么一直按着键图片会加快速度?

    Reply
  39. NPC

    关于缩进:最后五行语句都向前缩进一个TAB的距离

    Reply
    1. 张伟

      向后缩进逻辑不对, 我一开始就向后缩进了. 因为一说缩进就以为加Tab向后缩进, 初学精力光抄写代码了, 一点算法逻辑都考虑不了. 用了段时间想后, 才知道原代码最后从# 计算新的坐标 往下的代码的缩进根本都是bug代码, 因为把它们放在了for event循环下, 但它们不属于事件了, 是另外两件事情了, 更新图片位置和刷新屏幕, 它们是和event轮询同级别的,应该放到while循环下.
      但为什么放到for event下(尤其是更新图片位置的2句代码)会造成不能持续移动, 也没有报错, 这就是逻辑bug. 不能持续移动是因为, 更新位置和if KeyDown同级别, 都在for event下, 有事件才更新位置, 所以还会造成按住方向键移动鼠标会图片走这种bug, 根本就是bug代码. 还有些事项没讲完, 有兴趣的可以再思考一下.
      当然我讲的是代码逻辑了, 不是本教程的重点, 但编程学习应该是算法带动语法, 否则连做什么是都不知道, 光学语法了也记不牢

      Reply
  40. newraina

    试了一下,不用form sys import exit 也能正常退出

    Reply
  41. kroos

    太悲惨了,游戏玩多的后遗症,键盘移动的时候图片一直移动不了,纠结了半天,最后发现我一直按的是wasd,难怪不能移动,oh my god!

    Reply
  42. databatman

    总算搞懂为什么后五行缩进后会持续动了。。。就是当按下一个键不放后,event.get里面就只有一个事件,这时候执行完for之后会开始画图,画图完因为while的原因本来还要再执行for循环的,但是这时候的for里面的event.get里是没有事件的,所以for被跳过了,直接又开始执行后面的后五行,所以就产生了不断前进的效果。注:同时按下两个键的时候因为for里将这两个键的移动设置为1了,所以后五行会不断的按照这两个值来移动,直到key被放下,重新进入for,更新。

    Reply
  43. jackerb

    闲来无聊,发现有pygame这么个东西,拿来看看,发现博主的地盘,就跟着博主学学,看到这句话:如果你把填充色的(0, 0, 0)改为(0, 255, 0),效果会想黑客帝国的字幕雨一样。
    其实是真的,很像。这是我修改了一下的代码

    import pygame
    from pygame.locals import *
    from sys import exit
    import random
    
    pygame.init()
    SCREEN_SIZE = (640, 480)
    
    screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
    font = pygame.font.SysFont("arial", 18)
    font_height = font.get_linesize()
    event_text = []
    
    while True:
    	event = pygame.event.wait()
    	event_text = str(event)
    	
    	#event_text = event_text[-30:]
    		
    	if event.type == QUIT:
    		exit()
    		
    	screen.fill((0, 0, 0))
    	
    	y = SCREEN_SIZE[1] - font_height
    	
    	for text in event_text[::-1]:
    		if text.isalpha():
    		  x = random.randint(0, SCREEN_SIZE[0])
    		  screen.blit(font.render(text, True, (0, 255, 0)), (x, y))
    		  y -= font_height
    		
    	pygame.display.update()
    
    Reply
    1. 张旭帆

      我很喜欢你的这段程序,希望可以一起交流,我还是在校学生,可以的话加我qq注明在这个网站上的朋友就行了516259684,谢谢啦,渴望学习

      Reply
    2. ing

      之前我的我的代码有错,我照着你的改了,没有报错,但为什么弹出的Python Windows 没显示任何字母,我关闭时提示项目正在运行,这是什么原因?求解

      Reply
  44. wzzzx

    您好,我想请问下关于那个图片不断的动的问题.,按照我的理解,按下是一个事件,所以一直在for循环里,当松开的时候,又是一个事件,这时候就没有事件了,,所以跳出for循环,执行后五行.但是,图片要动起来,得松开之后才能动啊,为什么会一边按一边动??

    Reply
    1. xishui Post author

      这个动并不是靠事件驱动,而是在现有位置上不断加上一个偏移量,这个偏移量是由事件除非设置的,只要没有新的有效事件,这个偏移量就会不停作用在现有位置上。

      Reply
      1. 二三

        xishui老师的这段话一开始我也没看懂,后面把这些东西联系到一起才想明白,毫无疑问,xishui老师是非常专业的,我尝试着用自己的话解释一下,也是做个总结:
        在while True 循环里,for event 。。。会一直尝试从事件队列里取出事件来处理,如果没有事件就不做事件的处理工作(这不废话么),for event 。。。结束后 如果有后续代码要执行就接着执行后续代码。。
        按下按键时触发一个KEYDOWN事件,如果按下的按钮是上下左右中的一个,就设置一个运动值(偏移量),
        然后for event 。。。执行完毕,接下来执行
        x += m_x
        y += m_y
        screen.fill((0, 0, 0))
        screen.blit(fish, (x, y))
        pygame.display.update()
        这部分是while循环里的固定工作,无论x—m,x—y是0, -1 还是 1,都会在while循环里一直执行下去,事件处理只负责运动值(偏移量)的变动。

        如果这时按键没有松开,触发过的KEYDOWN事件已处理,事件队列里也没有新事件,while循环就会重复执行后五行代码,重复刷新工作。

        松开按键触发KEYUP事件,m—x,m—y 变成0,while循环重复刷新工作。

        over

        Reply
        1. 张伟

          是的, 是按while True这里的条件总是为真重复循环执行来刷新位置, 造成不断移动效果的. 是按cpu速度刷新屏幕的; 当然移动速度首先是由平移量设定的值决定, 但是cpu速度影响更大. 后面应该还需要讲 设定FPS, 设定fps是循环暂停一定绝对时间量, 比如设置为5, 移动速度就会大大减慢. 结论就是while和if条件都为真, 重复执行循环来造成移动

          Reply
  45. John

    有个问题想请教一下,我改了后五行的缩进,
    pygame.event.get()是得到所有的事件,举个例子,假如我就是按一下向上键(up),再松开,应该是两个时间,也就是说pygame.event.get()应该是[‘keyup’,’keydown’]这两个值,然后for循环开始生效,依次去判断event.类型,没问题,但是我们在for循环之上不是使用了一个while true:这个无限循环吗,我的意识也就是说执行完第一遍for循环,不是还应该执行第二遍for循环吗,直到执行到无数遍,一直循环下去,结果也就是我按一下然后再松开,图片也应该一直动

    Reply
  46. John

    查了python有关pygame的官网才懂,http://www.pygame.org/docs/ref/event.html#pygame.event.get,
    其实get.event()方法最后是要将序列中的message清空的

    Reply
  47. Y

    加了一句,以免移动到窗口显示以外去移动不回来了。
    if event.key == K_SPACE:
    x,y,move_x,move_y = 0,0,0,0

    Reply
  48. x=x+1

    好吧,我看得不够细心,没有看到这个缩进错误,毕竟不是一行一行看着打的,自己想当然就打了(按照第一课的逻辑,执行代码不会放在事件检测的for循环内)……另外,if…elif…群是不是可以用一个dict字典解决?我是用这两行:
    to_do = {K_LEFT:(-1,0),K_RIGHT:(1,0),K_UP:(0,-1),K_DOWN:(0,1)}
    #…
    moveX,moveY = to_do[event.key]

    Reply
    1. x=x+1

      按照我以前VB的逻辑,我一直在盼望着有一个KEYPRESS……这样就不用moveX,moveY了

      Reply
  49. Sam100

    将21、22行移到33行来让程序能捕捉退出事件。

    Reply
  50. 杨水月

    我有一个问题,在我的图片还没有移动出屏幕外时,我移动鼠标不会对桌面造成影响,当我的图片移动出屏幕窗口外之后,我移动鼠标,便会不停的画出鼠标的轨迹(鼠标我用带alpha的图片替代中),这是为什么?

    Reply
  51. 北方的绵羊

    C:\Users\Administrator\Desktop>Python pygame.py
    Traceback (most recent call last):
    File “pygame.py”, line 1, in
    import pygame
    File “C:\Users\Administrator\Desktop\pygame.py”, line 2, in
    from pygame.locals import *
    ImportError: No module named locals

    为什么会提示 No module named locals? 在第一讲中,代码里也有用到 from pygame.locals import *,在那里就没有问题,在这个第二讲中,就会提示这个。是什么问题呢?是我按楼主的代码,缩进有误?还想请教一个问题,写Python代码,用什么编辑器编辑适合初学者?谢谢!

    Reply
  52. Pingback: 用Python和Pygame写游戏-从入门到精通(2)-演道网

  53. candy

    博主我想问下为什么没改缩进前的那段代码运行的时候,按住某个方向键是只能移动一下的,但是我按住之后然后移动鼠标,图片就一直会往那个方向移动,这是为什么啊?另外我想请博主解释一下缩进之前不能一直移动的原因。。。。

    Reply
    1. 一颗赛艇

      pygame.event.get()是一直执行的,返回当前的所有事件,如果当前什么都没做就返回空list
      按照博主的代码,如果什么都没做,那么画面就不会更新(pygame.display.update()不会调用)
      当你按下方向键后只是设置了移动方向和移动距离,真正”移动”了几次看`for event in pygame.event.get():`循环次数
      如果你只按下了方向键让后什么都不做,那么循环只执行一次。但如果你有做了其他事(移动鼠标),此时·pygame.event.get()·返回不为空,鼠标移动事件触发了几次就循环了几次,也就“移动”了几次

      Reply
    2. 张伟

      这2个问题, 第二个问题按 代码的缩进逻辑慢慢推演并不难推导出来; 第一个问题就像bug一样, 真的是有价值的问题, 都没有注意到, 因为代码里并没有处理鼠标移动+方向键按下的代码, 怎么会能够持续移动, 同问

      Reply
    3. 张伟

      原来楼上同学已经回答了鼠标移动造成移动的原因, 第一次读没有读出来. 明白了

      Reply
      1. 张伟

        在监视键鼠event的for循环下加入 pygame.event.set_blocked(pygame.MOUSEMOTION)就能阻止鼠标移动的bug了

        Reply
        1. 张伟

          上面说错了, 是在if event.type == KEYDOWN:下面加入

          Reply
  54. Simona

    博主可以转载吗?会标明出处,因为自己又敲了一遍,加了些自己的注释等,想发布到自己的博客中去,望回复

    Reply
  55. hzw

    #获得时间的名称
    event_text = event_text[-SCREEN_SIZE[1]//font_height:]
    不知道你们的可不可以 我的除法需要双斜杠 是版本问题么?

    Reply
    1. limyel

      因为在Python3中‘/’得到的是真除法,可能会是浮点型,而‘//’是floor除法,得到的是整数

      Reply
  56. Pingback: 用Python和Pygame写游戏-从入门到精通(2) – 易可信blog系统

  57. Zigzag

    非常感谢博主的讲解

    但是处理键盘事件那里,因为pygame.event.get()的原因会导致长摁的连续移动会卡顿并斜行,可以在循环开头加入pygame.key.set_repeat( 10 )
    循环中刷新之前加入
    move_x = 0
    move_y = 0
    来解决。
    另外,该怎么让图片受控制的斜着走没有头绪,有没有朋友解答一下。。。

    Reply
  58. 油菜花

    发现一个奇怪的现象,键盘操作的那一段代码,当按住一个键不放是,鼠标在窗口内运动,背景也会移动,求解

    Reply
    1. 天天

      pygame.event.get()是一直执行的,返回当前的所有事件,如果当前什么都没做就返回空list
      按照博主的代码,如果什么都没做,那么画面就不会更新(pygame.display.update()不会调用)
      当你按下方向键后只是设置了移动方向和移动距离,真正”移动”了几次看`for event in pygame.event.get():`循环次数
      如果你只按下了方向键让后什么都不做,那么循环只执行一次。但如果你有做了其他事(移动鼠标),此时·pygame.event.get()·返回不为空,鼠标移动事件触发了几次就循环了几次,也就“移动”了几次

      这是之前其他人的回复,我搬运过来

      Reply
  59. XReSer

    为什么循环里的代码改成这样只能按一下动一下啊, 不能按住连续动, 看代码应该能连续动才对啊, 有没有大佬愿意解答一下
    while True:
    for event in pygame.event.get():
    if event.type == QUIT:
    sys.exit()
    if event.type == KEYDOWN:
    if event.key == K_UP:
    y -= 1
    elif event.key == K_DOWN:
    y += 1
    elif event.key == K_LEFT:
    x -= 1
    elif event.key == K_RIGHT:
    x += 1
    elif event.type == KEYUP:
    pass

    surf.fill((0, 0, 0))
    surf.blit(background, (x, y))

    pygame.display.update()

    Reply
    1. XReSer

      没有缩进了…最后三个语句是定格写的

      Reply
  60. XReSer
    import pygame
    from pygame.locals import *
    from sys import exit
    import random
    
    pygame.init()
    SCREEN_SIZE = (640, 480)
    
    screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
    font = pygame.font.SysFont("arial", 18)
    font_height = font.get_linesize()
    event_text = []
    
    while True:
    	event = pygame.event.wait()
    	event_text = str(event)
    	
    	#event_text = event_text[-30:]
    		
    	if event.type == QUIT:
    		exit()
    		
    	screen.fill((0, 0, 0))
    	
    	y = SCREEN_SIZE[1] - font_height
    	
    	for text in event_text[::-1]:
    		if text.isalpha():
    		  x = random.randint(0, SCREEN_SIZE[0])
    		  screen.blit(font.render(text, True, (0, 255, 0)), (x, y))
    		  y -= font_height
    		
    	pygame.display.update()
    
    Reply
    1. XReSer

      上面大佬的代码,我试一下怎么弄出的这种格式, 还没搞懂怎么回复…

      Reply
  61. XReSer

    为什么这样写不能按住连续移动啊

    import pygame
    from pygame.locals import *
    import sys
    
    pygame.init()
    SCREEN_SIZE = (640, 480)
    surf = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
    background = pygame.image.load('sushiplate.jpg').convert()
    x = 0
    y = 0
    
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            if event.type == KEYDOWN:
                if event.key == K_UP:
                    y -= 1
                elif event.key == K_DOWN:
                    y += 1
                elif event.key == K_LEFT:
                    x -= 1
                elif event.key == K_RIGHT:
                    x += 1
            elif event.type == KEYUP:
                pass
    
        surf.fill((0, 0, 0))
        surf.blit(background, (x, y))
    
        pygame.display.update()
    
    Reply
  62. hahahah

    Python版本3.6.1,在执行切片操作语句”event_text = event_text[-SCREEN_SIZE[1]/font_height:]”时,会报错,原因是单斜杠操作符表示浮点数除法,得到的结果是小数,将单斜杠改为双斜杠后程序成功运行

    Reply
  63. 章鱼

    import pygame
    pygame.init()
    screen_size=(640,480)
    screen=pygame.display.set_mode(screen_size,0,32)
    font=pygame.font.SysFont(“arial”,16);
    font_height=font.get_linesize()
    event_text=[]
    while True:
    event=pygame.event.wait()
    event_text.append(str(event))
    event_text=event_text[-screen_size[1]//font_height:]
    if event.type==pygame.QUIT:
    pygame.quit()
    screen.fill((255,255,255))
    y=screen_size[1]-font_height
    for text in reversed(event_text):
    screen.blit(font.render(text,True,(0,255,0)),(0,y))
    y-=font_height
    pygame.display.update()
    改进了一下

    Reply
  64. Mr hu

    本来我也想写一点pygame笔记分享到博客,看到楼主的博客后,觉得记笔记有点多余了,哈哈

    Reply
  65. 林田慧

    作者好啊,我是个小萌新。。。啥也不会,就看着你的教程一个字母一个字母的打了一遍代码,有很多东西不是很懂。
    这篇文章中的第一个程序,我在sublime中打完代码之后运行,在18行出现了这样的错误:
    TypeError: slice indices must be integers or None or have an __index__ method
    之后我试着把您的代码复制到sublime里运行,出现了同样的错误怎么办?
    还请指教指教

    Reply
    1. xishui Post author

      最好先学习一点Python基础,咱得先学习走路,再奔跑哇

      Reply
    2. 柴季

      切片[x:y:seq]里的x,y是起始,seq是步进,报错的原因是x和y、seq必须为整数或None,event_text[-SCREEN_SIZE[1]/font_height:],加个int()就可以解决问题,博主说的的对,你需要学习pythopn的基础

      Reply
  66. 柴季

    screen.blit(font.render(text, True, (0,0,0)), (0,y)),为什么y不可以是screen的height,却可以为0?

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

  68. 碧笛

    感谢博主和万能的网友!我想问的问题都找到答案了!来得早不如来得晚,哈哈哈哈!

    Reply
  69. Pingback: Python成神之路 - Pygame 教程:《用 Python 和 Pygame 写游戏 – 从入门到精通》

  70. 114514

    1. Pygame 2.0.1及以上版本中,JOYAXISMOTION、JOYBALLMOTION、JOYHATMOTION、JOYBUTTONDOWN、JOYBUTTONUP的joy属性已弃用,改为instance_id。

    Reply
    1. 114514

      2. 在使用SDL2编译的Pygame1.9.5及以上版本中,新增了一些事件,可以到https://www.pygame.org/docs/ref/event.html查看。

      Reply
  71. Pingback: python学习之 pygame模块 | Coding栈

  72. Pingback: pygame学习 | Coding栈

  73. Pingback: pygame学习_Johngo学长

  74. Pingback: python学习之 pygame模块_Johngo学长

碧笛进行回复 取消回复

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