《从零开始PYTHON3》第十五讲

虽然看起来绘图和音乐并不相关,但是听过了上一讲的内容你一定知道,这是游戏编程中四个需要处理内容的两部分,这两部分必须同时、并行的处理,不能因为某一项计算的拖延,导致另外一方程序的停滞。要知道人对声音的断续和游戏的卡顿是很敏感的。

在Pygame中进行并行处理的主要手段,一是Pygame中的各种函数,大多是不等待工作完成,只要工作开始进行,就返回主程序,等待下一条命令,而任务会在看不到的后端继续执行,并不停止;另外则是各个并行的任务之间,会通过“消息事件”的方式跟主程序沟通,从而让主程序能够统一调度各项任务的进程。

这是复习上一讲的内容。

并行:指的是在硬件的帮助下,多个任务同时进行,互不影响,最终完成任务的过程。完成的时间取决于最慢的任务。这个硬件帮助,通常是指多核CPU、显卡计算配合CPU计算以及数据传输中的多通道。

串行:指的是完成一项工作,才进行另外一项工作,最后完成的时间是所有任务完成的总和。


游戏绘图

绘图模式

同我们前面学过的科学绘图和海龟绘图相比,游戏绘图在绘图的模式上有较大的区别。

传统程序绘图是顺序方式,每画一笔可以认为这一笔一直都在,直到程序退出或者擦除画面。你可以回忆一下我们在科学绘图和海龟绘图时候所学习的内容。

游戏绘图更类似拍照,一个个角色进入画面,摆好姿态,等待快门按下,这样完成一帧。随后会根据游戏逻辑和输入,调整画面,再拍摄下一张,这样至少达到每秒30帧,才能达成一个动画的效果。

从逻辑上讲,游戏绘图采用的方式似乎应当慢于传统方式。实际上因为这种方式能够得到CPU/显卡以及很多新技术的帮助。很多绘图任务发出后,实际上是进入显卡完成运算的,这时候CPU已经在处理其它内容。这样并行计算的方式,再加上显卡更善于处理图形、图像相关的工作。最终这种方式效率才会高很多。

我们前面讲的科学绘图和海龟绘图,新版本的实现有很多是使用游戏绘图的方式,通过并行的方式完成计算。但因为用户编程接口的兼容性,所以至少从我们编程时所感受到的方式上,还是串行处理的。

坐标系

科技绘图(matplotlib):采用数学坐标系,同显示设备无关,通常原点在屏幕中心。绘图包会自动调整数学坐标系跟窗口分辨率的比例(窗口分辨率是可以在程序中设置的,只是前面的学习中我们基本使用了默认的设置),从而让显示效果最优。

海龟绘图(turtle):原点在窗口中心,跟数学坐标系方向相同,坐标是同显示设备分辨率相关的,但绘图的操作通常是用几何的方式,所以不用太担心显示设备本身的分辨率。

游戏绘图(pygame):原点在窗口左上角,x轴坐标向右侧增大,y轴坐标向下增大,最大值为屏幕分辨率。还有一些更底层的游戏绘图引擎,比如OpenGL会使用统一的1.0*1.0坐标系,然后在不同设备上映射成不同的分辨率。我们本讲的课程采用Pygame所使用的坐标体系。

颜色

在计算机中常用的颜色分类有这么几种:

  • 二值图:仅有黑白两色,比如字体库
  • 灰度图:0-255,共256级灰度,比如黑白照片
  • 伪彩色:0-255,共256种颜色,比如GIF动图、微信表情
  • 真彩色:RGB红绿蓝三色图,每种颜色0-255,按二进制计算,也称为24位色
  • 32位真彩色:RGBA四色,除了红绿蓝之外,A代表透明度,能表现更多的多种颜色互动、遮盖的效果

这些颜色格式Pygame都支持,但最新的游戏通常都已经采用32位真彩色的方式。

在游戏的显示过程中,如果不考虑透明度A的部分,所有颜色都是使用“三基色”来表达的,也就是红、绿、蓝,每个颜色分量可以的取值分为是0到255。0表示完全没有这个颜色,255表示这个颜色最强。当三个颜色都是0的时候显示为纯黑。当三个颜色都是255的时候,显示为纯白。

因为是三个颜色,所以通常的颜色都是使用三个值的“元组”的形式表达的。元组我们第九讲学过了。下面举一些例子,我们在程序中,预定义几个常用的颜色:

#黑色
BLACK =(0, 0, 0)
#白色
WHITE = (255, 255, 255)
#红色
RED = (255,0,0)
#绿色
GREEN =(0,255,0)
#蓝色
BLUE=(0, 0,255)

Pygame程序一般结构

上一讲和本讲开始我们都已经讲过,Pygame的主要工作模式是并行处理,其结构同传统的串行程序就必然有一些差别。这个差别并不大,很类似我们学习互联网编程时候的框架模板,即使不够理解,照抄下来用就可以。下面就是一个一般的结构:

#此代码仅为架构示例,没有具体功能
#作者:Andrew #引入扩展库
import pygame width=1280
heigh=556
color=32 #pygame初始化
pygame.display.init()
#创建一个绘图平面,后面参数为设定的窗口分辨率及颜色
screen = pygame.display.set_mode((width, heigh), 0, color)
#声音系统初始化
pygame.mixer.init() #1...其它自身初始化项目... #是否要退出标志
requireQuit = False
#程序主循环,在有退出申请之前一直循环
while not requireQuit:
#2...自己的绘图部分... #处理所有事件
for event in pygame.event.get():
#用户从窗口菜单选择退出
if event.type == pygame.QUIT:
requireQuit=True
break
#用户是否有按键?
elif event.type == pygame.KEYUP:
#为了可靠,只处理按键松开的动作
if event.key in [pygame.K_q,pygame.K_ESCAPE]:
#用户按了q键
requireQuit=True
break
#3...其它事件处理...
#4...其它程序逻辑... #优雅的退出,释放各种资源
pygame.mixer.quit()
pygame.display.quit()

上面的代码中,并不包含任何功能,只是一个模板。通常没有特殊需求的程序,只要编写其中的#1/#2/#3/#4部分的程序就可以。

为了程序更便于理解和阅读,还可以对上面的结构进一步的优化,比如把需要继续编程的部分函数化。当然函数化的时候要考虑到变量作用域,避免增加不必要的麻烦。


常用绘图功能

我们介绍几个常用的绘图功能,然后就可以代入到上面的模板代码中来实验了。

一般的几何图形绘制功能,都汇总在pygame.draw包中,比如:

  • 画圆:pygame.draw.circle
  • 矩形:pygame.draw.rect
  • 多边形:pygame.draw.polygon
  • 画线:pygame.draw.line
  • 画弧线:pygame.draw.arc
  • 画矩形:pygame.draw.rect

正常情况下,pygame的显示是在一个窗口中显示的(也可以根据需要设置全屏),窗口可以设置一个标题来表示你当前做的工作,这个命令是:

#设置窗口标题
pygame.display.set_caption('Hello World!')

用于显示的窗口默认是没有颜色,也就是黑色,可以设置窗口的底色:

#用白色填充窗口,既是设置窗口底色,也是把窗口清空,重新绘制下一帧
#pygame绘图是像摄影师拍摄每一帧的照片,还记得吗?
screen.fill(WHITE)

还有一些函数的功能,可以参考help(pygame)。help也可以查看某一个具体的子包,比如:help(pygame.draw)。下面我们通过程序示例代码来看看刚才讲的这些功能:

#我们定义一个函数,来完成画面的绘制
#避免过多的语句挤入到主循环中影响程序的结构
def draw(screen):
#2...自己的绘图部分...
#用白色填充窗口
screen.fill(WHITE)
#画多边形
pygame.draw.polygon(screen, GREEN, ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106)))
#画线
pygame.draw.line(screen, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(screen, BLUE, (120, 60), (60, 120))
pygame.draw.line(screen, BLUE, (60, 120), (120, 120), 4)
#画圆
pygame.draw.circle(screen, BLUE, (300, 50), 20, 0)
#椭圆
pygame.draw.ellipse(screen, RED, (300, 250, 40, 80), 1)
#矩形
pygame.draw.rect(screen, RED, (200, 150, 100, 50)) #使用直接操作图形缓存的方法在右下角画四个点
#这个功能比较底层,除非需要很专业的操作一般用不到
pixObj = pygame.PixelArray(screen)
pixObj[480][380] = BLACK
pixObj[482][382] = BLACK
pixObj[484][384] = BLACK
pixObj[486][386] = BLACK
pixObj[488][388] = BLACK
del pixObj #显示在屏幕上
pygame.display.update()

上面代码只列出了自己定义的绘图部分,其它部分需要融合到框架模板中去。完整的代码可以参考code2.py源文件。

下面的图片是绘制的效果:



程序运行之后,可以按q键退出程序,也可以从菜单选择Quit来退出。

老话题,想掌握学习的知识,只能多练习。

请在上面程序的基础,调整各项参数,增加或者减少绘图的指令,自己练练。看看谁绘制的画面最好看。


挑战

我们已经掌握了基本的绘图知识。可惜游戏没有这么简单,至少游戏需要是以动画的方式为基础,玩起来才会感觉到真实。

我们早已经说过,现代的游戏开发已经是一个团队配合的产物。不管想达成什么样的动画,一般都需要有美工专业人员完成原画的设计制作,提供成素材,随后才能由程序人员来完成让画面动起来的工作。

我们这里已经从网上下载了几个素材:



上面包含两个动画元素的素材,上面部分是一只小地鼠,仔细观察这四副图片,他们的脚在不同的位置。四张图片代表动画中的4帧,连续起来,就会出现小地鼠在跑的样子。

下面的箭比较简单,只需要一帧,箭的图片出现在屏幕不同的位置上,感觉起来就是箭飞到了那个位置。

如果你还记得第一讲的演示,你应当能看出来这些素材出自游戏Bunny。

下面我们编程序,来实现小地鼠从屏幕右侧快速跑到屏幕左侧的动画,和羽箭从屏幕左侧飞到右侧的动画。

#使用pygame对图片处理的功能,载入图片到变量
arrow = pygame.image.load("bullet.png")
#地鼠因为包含四帧,我们使用列表格式
badguy = [pygame.image.load("badguy.png"),
pygame.image.load("badguy2.png"),
pygame.image.load("badguy3.png"),
pygame.image.load("badguy4.png")]
#动画动起来,需要一帧帧的变化,下面的变量用于指当前显示的第几帧
badguyIndex = 0 #定义x1/y1和x2/y2两组坐标,
#分别用于表示羽箭和小地鼠在屏幕上的位置 #坐标系还记得吧?左上角是0,y向下变大,x向右变大
x1=0 #羽箭从左侧飞到右侧,开始x坐标是0,表示在左侧
y1=heigh/3#y坐标,在窗口上面的1/3位置 x2=width #小地鼠一开始在屏幕右侧
y2=heigh/3*2 #定义一个函数,用于计算向左移动时候下一个位置的坐标
def moveLeft(x):
x -= dx
if x < 0:
x += width
return x
#定义一个函数,用于计算向右移动时候下一个位置的坐标
def moveRight(x):
return (x+dx) % width #绘制的函数
def draw(screen):
screen.fill(WHITE) #白色填充窗口
screen.blit(arrow,(x1,y1)) #绘制羽箭
screen.blit(badguy[badguyIndex],(x2,y2)) #绘制地鼠
#显示在屏幕上
pygame.display.update() ... #4...其它程序逻辑...
#移动元素坐标位置的工作,应当放到“其它程序逻辑”中
#这样的方式使得程序逻辑,特别是绘图的逻辑干净易读 x2 = moveLeft(x2)
x1 = moveRight(x1)
badguyIndex = (badguyIndex+1) % 4 #地鼠下次使用下一帧

上面代码依然去掉了同前面重复的部分,完整的代码请参考code3.py程序。现在运行一下看看吧:



截图无法展示动态,你一定要亲自动手来试试,才能看到效果。关键点:

  1. 屏幕绘制部分,根据坐标值,绘制指定的图片。
  2. 在程序逻辑运算的部分,计算下一帧画面的时候,小地鼠和羽箭在屏幕上的新位置。以及地鼠的动画图片下次绘制采用哪一帧图片。

练习时间

  1. 修改上面程序的参数,让地鼠的速度加快一倍,而箭的速度保持不变
  2. 上一讲中的mp3播放器,请实现在播放器播放的时候,显示一张歌曲的封面图片

本讲小结

  • 本讲介绍了使用pygame绘制基本几何图形和绘制简单动画的方式
  • 绘画、动画其实都不难,重要的是画面的设计,只要有了连续的图片,就可以用数组的方式来实现连续动画
  • 对于一个规模越来越大的程序,想少出错、容易维护,就需要代码尽量规范、简洁、函数化

本课程至此就全部结束了。作为面对刚刚接触计算机软件编程的初学者课程,我们使用了15讲的篇幅,从Python的安装、命令行的互动计算开始,讲述了数学计算、程序逻辑控制、常用数据类型等基本Python编程的知识。接着又针对科技绘图、互联网编程、游戏编程等专业领域的应用做了讲解。时间所限,我们并没有能够特别深入对所有话题更进一步的学习。课程也没有对当前流行的面向对象编程做讲解,这些有待于学习者在对初级内容有了一段时间的熟悉和体验之后,继续深入学习。

希望各位同学能在当前学习的基础上,根据自己的爱好和自己的日常学习、生活需要。有选择的做进一步学习,让Python成为我们学习的好助手,生活的得力工具。

水平所限,课程内容难免疏漏、错误,敬请谅解并欢迎指正。


练习答案

请参考代码:mp3Player1.py

  • 连载正文结束,所有程序源码及练习答案将会整理后在下一期提供下载。

从零开始学习PYTHON3讲义(十五)让画面动起来的更多相关文章

  1. 从零开始学习PYTHON3讲义(五)while循环和棋盘麦粒问题

    <从零开始PYTHON3>第五讲 ​上一节课重点学习了字符串,并且传递了一个重要的理念,就是程序要对开发人员自己和用户都足够友好.在这个过程中,利用字符串给出充分.完整.准确的提示是非常重 ...

  2. 从零开始学习PYTHON3讲义(一)认识Python

    课程名称 从零开始PYTHON3 课程长度 15讲 适用年龄 15-20岁(初三-大一) 本讲名称 认识Python 时长 90分钟 教学内容分析 Python是时下最流行的计算机编程语言之一.本课程 ...

  3. 从零开始学习PYTHON3讲义(十四)写一个mp3播放器

    <从零开始PYTHON3>第十四讲 通常来说,Python解释执行,运行速度慢,并不适合完整的开发游戏.随着电脑速度的快速提高,这种情况有所好转,但开发游戏仍然不是Python的重点工作. ...

  4. 从零开始学习PYTHON3讲义(十二)画一颗心送给你

    (内容需要,本讲使用了大量在线公式,如果因为转帖网站不支持公式无法显示的情况,欢迎访问原始博客.) <从零开始PYTHON3>第十二讲 上一节课我们主要讲解了数值计算和符号计算.数值计算的 ...

  5. 从零开始学习PYTHON3讲义(十)自己做一个“电子记事本”

    <从零开始PYTHON3>第十讲 截至上一讲,我们已经完成了Python语言的基本部分.我们用了三讲来讨论Python语言的控制结构,用了两讲来介绍Python的基本数据类型.可以说仅就语 ...

  6. 从零开始学习PYTHON3讲义(十六)(连载完)学习资源包下载

    <从零开始PYTHON3>学习资源包下载 课程连载已经完全结束. 经过整理校对,这里把在课程中出现过的源码和练习答案示例源码全部打包提供下载: https://pan.baidu.com/ ...

  7. 从零开始学习PYTHON3讲义(十三)记事本的升级版:网络记事本

    <从零开始PYTHON3>第十三讲 网络编程的火热和重要性这里就不多说了,我们直接来看看Python在互联网编程方面的表现. Python有很多网络编程的第三方扩展包,这里推荐一个我认为最 ...

  8. 从零开始学习PYTHON3讲义(十一)计算器升级啦

    (内容需要,本讲中再次使用了大量在线公式,如果因为转帖网站不支持公式无法显示的情况,欢迎访问原始博客.) <从零开始PYTHON3>第十一讲 第二讲的时候,我们通过Python的交互模式来 ...

  9. 从零开始学习PYTHON3讲义(三)写第一个程序

    <从零开始PYTHON3>第三讲 本页面使用了公式插件,因博客主机过滤无法显示的表示抱歉,并建议至个人主页查看原文. ​ 我见过很多初学者,提到编程都有一种恐惧感,起源是感觉编程太难了.其 ...

随机推荐

  1. 科学计算工具Numpy

    参考学习资料: Python.NumPy和SciPy介绍:http://cs231n.github.io/python-numpy-tutorial NumPy和SciPy快速入门:https://d ...

  2. Python 三级菜单 增强版

    需要实现的功能是:三级菜单1.从文本内读出选项2.查询每一级的选项,并能对选项进行增/删/改功能3.每一级可以退出程序或者返回上一层 2018-5-14 更新内容 思路 实现过程中的BUG及解决方案: ...

  3. 关于xpath相对路径前加点与不加点的区别

    转自:https://blog.csdn.net/qingmu_9923/article/details/51771602 最近在用selenium做web工程自动化测试的相关项目,会经常用到元素定位 ...

  4. Kafka元数据缓存(metadata cache)

    经常有人问的一个问题就是:Kafka broker到底是不是无状态的?网上有这样的说法: 正常情况下consumer会在消费完一条消息后线性增加这个offset.当然,consumer也可将offse ...

  5. BZOJ_3223: Tyvj 1729 文艺平衡树 _splay

    题意: 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 分析: ...

  6. JSON 数据重复 出现$ref

    JSONArray  类型  如果我们往里面add数据的时候 如果数据相同,那么就会被替换成 $ref:   也就是被简化了 因为数据一样所直接 指向上一条数据 循环引用:当一个对象包含另一个对象时, ...

  7. QTTabBar

    出处:https://www.mokeyjay.com/archives/1811

  8. 【原创】分布式之redis复习精讲

    引言 为什么写这篇文章? 博主的<分布式之消息队列复习精讲>得到了大家的好评,内心诚惶诚恐,想着再出一篇关于复习精讲的文章.但是还是要说明一下,复习精讲的文章偏面试准备,真正在开发过程中, ...

  9. volitale、synchronized、RetreenLock区别

    synchronized和RetreenLock锁区别 1.synchronized是java关键字,RetreenLock是个java类 2.synchronized无法获取锁状态,Lock可以判断 ...

  10. 神奇的Scala Macro之旅(四)- BeanBuilder

    在Java开发中,经常会有一个需求,将一个 Bean 复制到另外一个 Bean,尤其是在后台分层的场景下,在不同的层之间传递信息,经常需要进行 这样的一个对象复制工作,类似于: val source: ...