之前我写过一篇介绍入门PyGame的帖子:
忘荃的趣味编程:把Pygame看作processing的升级版zhuanlan.zhihu.com在入门PyGame的短暂快乐过后,我马上认识到了一个严重的问题。虽然Pygame在交互上简洁而强大,所以被公认是python生态中用来做小游戏(非商业游戏)的不二选择,但是大家在做游戏的时候普遍做法是直接给一个小物体(spirite)使用一个图片作为贴图的。可以说,用Pygame做出来的游戏本质都是一堆披着图片的 spirite 在跑来跑去。
但是,我们这些 processing er 们才不屑于和一堆图片打交道,我们的工具箱里都是点线面,贝塞尔曲线,椭圆方块,自定义多边形,stroke,fill ,多图层,camera 。这种和计算机图形学紧密相连的渲染方式更底层,表达能力也更强大。但是PyGame中没有这么高级的渲染能力,甚至连贝塞尔曲线都没有,连抗锯齿的几何图形都还只是实验性功能。如果你真的用它来创作一个视觉艺术作品,那最后的作品十有八九会让你自己都觉得“丑爆了”。
这个时候,救场的英雄登场了。他就是可爱的PyCario。PyCario是Cario的python接口,而Cario则是一个大名鼎鼎的开源多平台2D矢量图形库,支持做各种复杂的点线图案绘制、填充、文字渲染、图像变换、剪切、层混合等等操作。
因为能够提供高级的跨平台2D绘图的支持,cairo在开源社区十分流行(来自wiki)。它是matplotlib的重要后端之一,而且Cairo在目前的桌面环境中,占有非常重要的地位。Linux的两大流行桌面环境KDE和Gnome,其对应的基础组件是QT和GTK+,而cairo就是GTK+采用的底层图形库。Mozilla 项目也在最近版本的Gecko排版引擎中使用了cairo,用来渲染生成的图形输出。
说了这么多,无非就是想说PyCario十分可靠且强大,而且刚好和pygame能够完美配合。PyGame负责交互和资源读取,PyCario则完全负责图形渲染。在完全实现processing功能的同时,还和python生态完全兼容,方便嵌入到各种python程序中,且支持再各种python的IDE中进行“快乐开发”和“快乐调试”。
入门资源
安装:windows下建议从这个网站 下载最新版PyCario并使用python setup.py install 安装。
首先建议看一下这个PyCario入门教程:链接 。读完之后,你会发现它的非常多的函数都和processing非常像(比如也提供了transform方法),但是它的基本过程是这样的:
在找到一个surface后,先选一个Source(Source可以是纯色,也可以是渐变色,甚至可以是一个图片或者已有的surface),再选一个Path(Mask),最后再通过stroke或者fill 画出来。当你选择纯色的Source,一个圆形作为Path,最后再stroke,就能够得到一个空心的圆形。整个过程可以理解为图形的形状来自Path,而图形各部分的颜色来自Source。
其画图的过程和processing非常相似,但是也有细节不同(比如说坐标值和颜色值就采用了小数表示法,取值从0到1)。想要了解更多的话,建议浏览官方的小例子集合:链接 。这个链接中所以的例子都默认你已经有了一个surface,那么我们的surface在哪里呢?
答案是由pygame提供,pygame中的每一帧都可以对应到一个PyCario的surface。PyCario官方提供了使用一个将pygame和PyCario结合再一起使用的例子:链接 。通过更改这个程序中的draw函数,我们就可以实现自己的程序啦~
比如说我是这么修改的( 作品思路“抄袭”《The Nature of Code》的例8.2:两次递归):
def drawCircle(cr, x, y, r):
cr.arc(x, y, r, 0.0, 2.0 * math.pi)
cr.stroke()
if r > 0.004:
drawCircle(cr, x + r, y, r / 2)
drawCircle(cr, x - r, y, r / 2)
drawCircle(cr, x, y + r, r / 2)
drawCircle(cr, x, y - r, r / 2)
def draw(surface):
ctx = cairo.Context(surface)
ctx.scale(width, height)
# 画图背景
ctx.set_source_rgb(1, 1, 1)
ctx.rectangle(0, 0, 1, 1) # Rectangle(x0, y0, x1, y1)
ctx.fill()
# 开始画圆
ctx.set_source_rgb(1, 0, 0)
ctx.set_line_width(0.001)
drawCircle(ctx, 0.5, 0.5, 0.25)
完整代码:链接
动态例子
作品思路“抄袭”《The Nature of Code》的例6.04:FlowField):
代码链接:链接
使用到的非标准库:cario, pygame, noise
程序特点:
将cario的画图功能进行了封装,变成了processing玩家熟悉的circle, backgroudn, fill, translate等函数。并将processing玩家熟悉的全局变量暴露了出来,如width, height, mouseX, mouseY 等。将Pygame的Vector2改造成了processing在2D场景中常用的PVector。将主要画图功能依旧保持在update()函数内(相当于processing中的draw函数)。
我还准备了该程序的pyprocessing版实现:链接 。感兴趣的同学可以自己比较一下两个程序的相似程度(非常相似)。
还存在的主要问题:pygame和pycario的颜色通道不一致,所以使用了单色画面,下一步将会纠正一下。
修正颜色通道
总的来说,从pycario的surface到pygame的surface产生的颜色通道异常是一个持续了很多年的BUG。或许不应该叫BUG,因为只是两者的接口不一致,所以它才持续存在了N年至今。虽然如此,但是还好pycario和pygame都是有名的常用库,所以自然也有不少的人遇到了这些问题,比如这里 。
这些答案明确地指出,两者的颜色通道顺序分别是‘BGRA’和‘RGBA’,只要在buffer中调换R和B就可以了。但是解决方案都是基于python2的,有些过于老旧,导致运行程序出错。经过我的微调,最近解决方案终于新鲜出炉(需要再安装pillow库),并且我还顺便改进了例程:加入了彩色,拖尾等,所以一定要看看最后的视频~
改版程序链接:地址