scene — 2D游戏和动画
scene
模块提供了一种简便的方法来创建硬件加速的2D图形和动画,尤其是游戏。
概述
简介
对于每个基于scene
的项目,首先必须创建一个Scene
子类,该子类负责绘制内容,响应触摸事件等。要实际获得一个屏幕上的场景,请实例化子类并将其传递给run()
函数。Scene
类提供了各种方法,你可以重写来定制其行为,例如Scene.setup()
,在你的场景被显示在屏幕上之前调用:
1 | from scene import * |
在上面的最简单的示例中,Scene.setup()
方法只是将场景的背景色设置为绿色。
要向场景中添加实际内容,通常需要创建Node
对象- Node
对应于不同内容的几个不同的子类,例如SpriteNode
渲染图像,LabelNode
文本等。Scene
也是的Node
子类,并且节点可以包含其他节点(形成树或“场景图”)。这对于将一组对象作为单个实体移动或旋转通常很有用。所有节点都拥有position
,rotation
,scale
和alpha
(不透明度)属性,以决定该节点及其孩子如何绘制。默认位置是(0, 0)
对应于屏幕的左下角。
在下面的示例中,setup()
方法被扩展为在屏幕中央添加一个太空飞船:
1 | from scene import * |
这里需要注意几件事:
- 传递给
SpriteNode
初始化程序的字符串是内置图像的名称。你可以使用编辑器右下的[+]
按钮访问Pythonista中的内置图像。 - 为了方便起见,表示
scene
模块中尺寸和位置的对象支持几个标准运算符-在这种情况下,场景的大小(在setup()
调用之前会自动设置)仅需除以二,从而得到屏幕的中心。尺寸和点可以互换使用,因此为position
属性赋值尺寸是可行的。你也可以使用两个数字组成的简单元组。 - 默认情况下,
SpriteNode
的position
属性与其中心相对应。在某些情况下,改为设置子画面的一个角的位置可能更方便-你可以使用anchor_point
属性更改此行为。
触摸和简单动画
让我们再次扩展该示例以使其响应触摸。为此,我们只需重写Scene
的touch_began()
方法:
1 | from scene import * |
最后一个示例介绍了Action
类。动作可让你轻松为Node
的属性设置动画,在这种情况下为其位置。计时模式(TIMING_SINODIAL
)指定开始值和结束值之间的插值类型。如果忽略此参数,则会获得默认(线性)插值,从而导致更直接的运动。还有许多其他Action
工厂方法可用于旋转或缩放节点,更改其不透明度(alpha
)甚至从其父节点中删除它们。你还可以使用Action.group()
和Action.sequence()
组合多个动作,或使用Action.repeat()
来创建重复另一个动作的动作。
与touch_began()
类似,有一些方法可以检测触摸何时移动或结束– touch_moved()
和touch_ended()
。
高级动画和运动控制
虽然Actions
提供了易于使用的高级API来为场景的内容设置动画,但你也可以通过重写Scene
的update()
方法来逐帧地对画面更改进行动画处理。下面的示例通过响应设备的方向(使用gravity()
功能)移动太空船来说明这一点。默认情况下,update()
方法每秒调用60次,因此直接设置飞船的位置,而不是依赖于基于动作的动画:
1 | from scene import * |
此处使用的gravity()
函数提供了一种非常易于使用的确定设备方向的方法。为了获得更细粒度的控制,你可能需要使用motion
模块。在运动控制的游戏中,自动旋转可能会很烦人,因此使用run()
函数的附加参数将该场景限制为纵向。
作为反馈,当你触摸屏幕时,太空飞船现在会发射激光。初始化激光精灵时设置z_position
属性,确定绘制顺序–此处将其设置为-1,以使激光不会出现在船顶。将激光添加到场景后,它将执行两个动作序列:第一个动作将其向上移动1000个像素点,然后使用特殊的删除动作将其从场景中删除(这实际上不是动画,但提供为一个Action
,这样它就可以成为动画序列的一部分)。
这只是一个非常基本的概述–你可以使用scene
模块做更多的事情。随附的“Examples
”文件夹包含几个完整的游戏,你可以用来学习更高级的技术。
几何
场景的坐标系的原点(0,0)
在左下角。
概述中仅作简单提及,scene
模块提供了几个方便的类,用于表示2D几何形状,主要是Rect
,Point
和Size
。后两者基本上是相同的,但用于不同的上下文。
这些几何对象用于各种Node
属性,如Node.position
,SpriteNode.size
,Node.frame
等。当你给这些属性分配一个新值,你不必创建Point
或Size
的明确对象,-两个数字的任何序列(例如,一个元组或列表)将起到相同的效果。
所有这些类的行为都类似于序列,因此你可以使用例如point[0]
访问Point
的x
坐标(尽管point.x
通常使用起来更方便)。
对于Vector2
,Point
和Size
,为了方便,实现了一些数学运算的支持:
- 将向量与标量(即数字)相乘会将两个分量都与该数字相乘。划分也是如此。例如
Size(100, 200) * 0.5
,结果为Size(50, 100)
。 - 将两个向量相乘会将对应位相乘,例如
Point(100, 100) * (2, 3)
导致Point(200, 300)
。 - 将向量添加到另一个向量的行为相同,例如
Point(100, 100) + Point(20, 50)
,结果为Point(120, 150)
。
为了进行碰撞检测,可以使用in
运算符检查Point
是否在Rect
中。要检查一个矩形是否与另一个矩形相交,可以使用方法RectRect.intersects()
(或者Rect.intersection()
,如果需要确切知道矩形的相交位置)。
颜色
代表Node
颜色的属性,例如SpriteNode.color
或Scene.background_color
,可以通过多种方式设置:
- 作为HTML十六进制颜色字符串,例如’
#ff0000
‘为红色。 - 作为一个CSS颜色名称,例如’
green
‘,’blue
‘,’magenta
‘… - 由3或4个数字组成的元组,对应于0.0-1.0范围内的红色,绿色,蓝色和
alpha
分量。alpha
分量默认为1.0,即完全不透明。 - 一个介于0.0和1.0之间的数字,代表灰度值(0.0 =黑色,1.0 =白色)。
无论你如何设置颜色属性,将其读回都将始终生成4元组(r,g,b,a)
。
经典渲染循环
除了上述基于节点的方法外,scene
模块还支持经典的渲染循环,该循环是Pythonista早期版本中的主要接口。在大多数情况下,基于节点的场景将显着提高性能,但是如果你已经知道类似的编程环境(例如处理),则经典渲染循环可能会更容易理解。
要使用此模式,你还必须创建Scene
的子类,但是你无需重写节点,只需重写draw()
方法即可。每帧都调用此方法(即每秒60次),并且你可以使用模块级绘制函数在每帧中“绘制”你的内容。用这种方法在屏幕上绘制的图像和形状不会在一帧到下一帧之间保留下来–在draw()
方法的每次调用中,你基本上都是从空白屏幕开始的。
与经典OpenGL相似,你可以在调用实际的绘图函数之前设置全局状态变量(例如填充颜色和坐标转换)。以下示例绘制了几个旋转的矩形:
1 | from scene import * |
如果你使用的是Pythonista的早期版本,则你可能还熟悉与Node
和Action
概念上类似的Layer
和 Animation
类。这些类仍可用于向后兼容,但强烈建议改用新的 Node
/Action
API,因为它们可提供更好的性能。
注意: 在此模式下可以用于绘图的功能记录在
scene_drawing
模块中,但是如果要使用它们,则不必显式导入scene_drawing
。
与ui模块集成
运行场景的最简单方法是使用run()
函数,该函数可以全屏显示场景。有时这不是你想要的,尤其是当你构建的场景实际上不是游戏时。为了获得更大的灵活性,你还可以SceneView
显式地创建一个显示控件,将其scene
属性设置为要呈现的场景,然后将其添加到使用ui
模块创建的视图层次结构中。
你也可以使用这种方法将传统的UI元素(例如,文本字段)添加到游戏中。为此,你不必创建SceneView
-你只需使用Scene
通过自动创建的view
属性设置视图即可(当然,只有在场景已经呈现的情况下,该视图才起作用)。
ui
模块还可用于渲染要用作纹理的矢量图像(形状,文本等)。为此,Texture
可以从ui.Image
初始化,通常是你使用ui.ImageContext
创建的。在ShapeNode
和LabelNode
类用这种方式来渲染的内容。
游戏控制器
scene
模块支持使用MFi游戏控制器(如Nimbus SteelSeries游戏手柄)控制游戏。
在游戏中支持硬件控制器输入非常简单–你基本上只需重写Scene
子类中的Scene.controller_changed()
方法即可响应控制器事件(例如,按下/释放按钮)。该方法获得两个参数:key
,这是一个字符串,代表已更改的控制器元素(例如’button_a
‘),以及控制器元素的当前值。值的类型取决于控制器元素的类型–对于基本按钮,它是一个布尔值;对于压力敏感型触发器,其浮点值介于0.0到1.0之间;对于dpad和指尖,它是一个Point
对象(带有x
和y
值,在-1.0到+1.0之间)。
在某些情况下,在Scene.update()
方法或其他地方查询控制器的当前状态会更方便。为此,你可以使用模块级get_controllers()
函数。这将返回一个字典列表,这些字典表示每个当前连接的控制器的所有按钮,dpad,指尖等的当前状态。
节点类
场景
class scene.Scene
场景是节点树中的根节点(Node
对象)。这些节点提供场景动画并渲染以供显示的内容。要显示场景,通常需要调用模块级run()
函数以全屏形式显示它。如果要在其他UI内容中显示场景,也可以显式创建SceneView
。
场景通过依次处理以下动作来计算新帧的内容:
- 场景调用其
update()
方法。 - 场景对其子代执行动作。
- 场景调用其
did_evaluate_actions()
方法。 - 场景将渲染其所有节点,并更新视图以显示新内容。
你通常会创建至少一个Scene
子类来处理触摸事件(通过重写touch_began()
,touch_moved()
,touch_ended()
)与你的游戏内容进行各种交互。
请注意,Scene
是EffectNode
的子类,因此你可以对全屏处理后的效果使用自定义Shader
。但是与普通的EffectNode
不同,场景的effects_enabled
属性默认情况下设置为False
。
Scene.setup()
在场景在屏幕上呈现之前,将调用一次。你可以使用它来设置场景。此时size
和bounds
属性已经是有效的了,这样你就可以使用它们来确定你的内容的布局。
Scene.touch_began(touch)
当在场景视图上开始触摸时,将调用此方法。通常,你不直接调用它,而是将其实现为Scene
子类的一部分。
你可以检查触摸对象的location
属性,以获取触摸在场景坐标系中的位置。要区分同时触摸,可以使用触摸的touch_id
属性。
Scene.touch_moved(node, touch)
当触摸在场景视图中移动时,将调用此方法。通常,你不直接调用它,而是将其实现为Scene
子类的一部分。
你可以检查触摸对象的location
属性,以获取触摸在场景坐标系中的位置。要区分同时触摸,可以使用触摸的touch_id
属性。
Scene.touch_ended(node, touch)
当触摸在场景视图中结束时,将调用此方法。通常,你不直接调用它,而是将其实现为Scene
子类的一部分。
你可以检查触摸对象的location
属性,以获取触摸在场景坐标系中的位置。要区分同时触摸,可以使用触摸的touch_id
属性。
Scene.did_change_size()
每当场景大小改变时,通常是在屏幕旋转时,都会调用此方法。通常,你可以重写以重新定位内容位置。调用时,场景的size
属性已经设置为新的大小。
Scene.did_evaluate_actions()
场景完成处理其子节点的动作后,将调用此方法。
Scene.update()
执行在计算场景动作之前需要进行的所有特定于场景的更新。
不要直接调用此方法;只要场景在视图中呈现并且不暂停,每帧它都会被调用一次。默认情况下,此方法不执行任何操作。你的场景子类应重写此方法,并对场景执行任何必要的更新。
Scene.pause()
在场景运行过程中按下主页按钮时,将自动调用。例如,你可以覆盖此设置以保存持久状态。默认实现不执行任何操作。
Scene.resume()
恢复场景时(使用主屏幕按钮切换到后台后)自动被调用。默认实现不执行任何操作。
Scene.stop()
当场景停止时(通过点击“x
”按钮)自动被调用。你可以覆盖此设置以保存持久状态。默认实现不执行任何操作。
Scene.present_modal_scene(other_scene)
在此之上呈现另一个场景。这对于覆盖菜单等很有用。在呈现场景时,它会接收所有触摸事件。
Scene.dismiss_modal_scene()
使用来关闭通过Scene.present_modal_scene()
显示的场景。
Scene.controller_changed(key, value)
当任何已连接的游戏控制器的状态发生更改时(例如,在按下/释放按钮时,或者在指尖或dpad之一的方向发生更改时),都会自动调用此方法。
key
参数是指定控制器的元件更改的字符串(例如’button_a
‘,’dpad
‘,’thumbstick_left
‘…),value
包含该元素的当前值。value
的类型随控制器元素的类型而变化。对于方向性输入元素(dpad,指尖),该值是一个Point
对象。对于其他对象,它可以是浮点数(当元素是压力敏感元素时)或布尔值。
场景的属性
scene.bounds
具有原点(0,0)和可绘制区域大小的Rect
。
Scene.dt
自上次调用update()
以来经过的时间(以秒为单位)。你可以使用它来计算自定义动画的进度。
Scene.size
整个可绘制区域的大小。
Scene.t
自场景开始以来经过的时间(以秒为单位)。你可以使用它来计算自定义动画的进度。
Scene.touches
当前有效的所有触摸的字典。键对应于Touch
对象的touch_id
属性。
Scene.background_color
场景的背景色。
默认值为深灰色。
Scene.size
场景的尺寸(以磅为单位)(只读)。这对应于场景视图的大小。
Scene.view
在当前场景呈现的View
。如果当前未显示场景,则为None
。(只读)
Scene.presented_scene
当前使用Scene.present_modal_scene()
正在呈现的场景(如果没有则为None
)。
Scene.presenting_scene
呈现场景,如果此场景正由另一个场景呈现(使用present_modal_scene()
)。
节点
class scene.Node([position=(0, 0), z_position=0.0, scale=1.0, x_scale=1.0, y_scale=1.0, alpha=1.0, speed=1.0, parent=None])
Node
类是场景的基本构建模块。基本Node
类本身不会画任何东西,它的主要作用是提供其子类使用的基线行为。节点也可以包含其他节点,以将其修改为一个组。为了绘制实际内容,通常会使用Node
的子类之一:
SpriteNode
– 绘制纹理精灵的节点LabelNode
– 专门用于呈现文本字符串的SpriteNode
。ShapeNode
– 专门用于根据贝塞尔曲线路径渲染形状的SpriteNode
(例如,圆形或圆角矩形)。EffectNode
– 使用自定义着色器对其子级应用特效的节点。
节点按层次结构组织到节点树中,类似于视图和子视图的工作方式。最常见的是,定义一个节点树,其中一个Scene
节点为根节点,其他内容节点为后代。Scene
节点运行一个动画循环,该循环处理节点上的动作,然后渲染节点树的内容以进行显示。
节点树中的每个节点都为其子节点提供一个坐标系。将子级添加到节点树后,可通过设置其位置属性将其定位在其父级的坐标系中。一个节点的坐标系统可以缩放和旋转通过改变其x_scale
,y_scale
和rotation
属性。当缩放或旋转节点的坐标系时,此变换既应用于节点自身的内容,也应用于其后代的内容。
Node
类自己不执行任何绘图操作。但是,许多子类呈现视觉内容,因此Node
类理解一些视觉概念:
frame
属性提供了节点的可视内容的边界矩形,并通过scale
和rotation
属性对其进行了修改。如果节点的类绘制内容,则框架不为空。每个节点子类都会以不同方式确定此内容的大小。在某些子类中,例如在SpriteNode
类中,显式声明了节点内容的大小。在其他子类中,内容大小由该类使用其他对象属性隐式计算。例如,LabelNode
对象使用标签的文本和字体特征确定其内容大小。
节点的bbox
是最大矩形包括该节点的框架及其所有后代框架的框架最大矩形。
其他属性(如alpha
属性)会影响节点及其后代的绘制方式。
树中的任何节点都可以运行动作(Action
对象),这些动作(对象)用于为节点的属性设置动画,例如将其平滑地移动到新位置。
Node.add_child(node)
将一个节点添加到接收者的子节点列表的末尾。
Node.remove_from_parent()
从其父节点中删除该节点。
Node.remove_action(key)
结束并从节点中删除由其键标识的特定操作。key
是传递给Node.run_action()
方法的任意字符串。
Node.remove_all_actions()
结束并从节点中删除所有操作。
从节点删除动作后,该动作将执行的所有剩余动画都会被跳过;但是,以前的更改不会恢复。
Node.render_to_texture([crop_rect])
通过渲染此节点及其子节点的快照来创建Texture
对象。
Node.point_to_scene(point)
将一个点从该节点的坐标系转换为其所在场景的坐标系。如果该节点不是场景的一部分,则引发ValueError
。
Node.point_from_scene(point)
将一个点从该节点场景的坐标系转换为该节点的局部坐标系。如果该节点不是场景的一部分,则引发ValueError
。
Node.run_action(action[, key])
将操作添加到节点执行的操作列表中。
如果使用相同键(任意字符串)的操作已在运行,则在添加新操作之前将其删除。
节点的属性
Node.bbox
在父级坐标系中计算一个矩形,该矩形包含节点及其所有后代的内容。
Node.alpha
应用于节点内容的透明度值。
Node
类不进行绘图,但许多它的子类的要。绘制节点或其任何后代时,每个像素的alpha
分量乘以节点的alpha
属性,然后限制在0.0-1.0
范围内。修改后的alpha
值用于将像素混合到帧缓冲区中。呈现内容的子类定义一些属性,这些属性确定与alpha
值结合使用的混合操作,以将像素混合到父级的帧缓冲区中。
Node.frame
父级坐标系中的一个矩形,其中包含节点的内容,而忽略节点的子级。(只读)
Node.children
此节点的子节点的列表。请注意,修改此列表无效-使用Node.add_child()
和Node.remove_from_parent()
代替。
Node.parent
节点的父节点。(只读)
Node.paused
一个布尔值,该值确定是否处理该节点及其后代的操作。
Node.position
节点在其父级坐标系中的位置(x,y)。
Node.scene
包含节点的场景节点。(只读)
如果节点未嵌入场景中,则值为None
。
Node.speed
速度修改器应用于节点及其后代执行的所有操作。
Node.x_scale
比例因子,乘以节点及其子节点的宽度。
x_scale
属性缩放节点及其所有后代的宽度。比例值会影响节点框架的计算方式,命中测试区域,绘制方式以及其他类似特征。默认值为1.0。
Node.y_scale
比例因子,乘以节点及其子节点的高度。
y_scale
属性缩放节点及其所有后代的高度。比例值会影响节点框架的计算方式,命中测试区域,绘制方式以及其他类似特征。默认值为1.0。
Node.z_position
节点的z轴位置确定其相对于其同级元素绘制的顺序。预设值为0.0;值较大的节点将呈现在值较小的节点之前。
Node.rotation
节点围绕z轴的旋转(弧度)。
默认值为0.0,表示不旋转。正值表示逆时针旋转。旋转坐标系时,它会影响节点及其后代。
精灵节点
class scene.SpriteNode([texture, position=(0, 0), z_position=0.0, scale=1.0, x_scale=1.0, y_scale=1.0, alpha=1.0, speed=1.0, parent=None, size=None, color=’white’, blend_mode=0])
SpriteNode
是绘制纹理图像,彩色正方形或混合有颜色的纹理图像的节点。你还可以提供自定义着色器以创建自己的渲染效果。
初始化SpriteNode
时,你可以提供纹理作为Texture
对象,也可以提供内置图像或图像文件的名称(字符串)。
精灵节点的属性
SpriteNode.anchor_point
定义精灵中与节点位置相对应的点。
你可以在单位坐标空间中为此属性指定值。默认值为(0.5,0.5),这意味着子画面以其位置为中心。
SpriteNode.blend_mode
混合模式用于将精灵绘制到父级的帧缓冲区中。
混合模式中列出了此属性的可能值。默认值为BLEND_NORMAL
。
SpriteNode.color
精灵的颜色。
如果设置了texture
属性,则图像将以给定的颜色着色。如果texture
属性为None
,则颜色用于绘制彩色矩形。
SpriteNode.shader
一个属性,用于确定是否使用自定义着色器渲染精灵。
默认值为None
,这意味着将执行精灵渲染的正常行为。如果将Shader
附加到此属性,则将使用自定义着色器渲染精灵。
SpriteNode.size
精灵的尺寸,以点(宽度,高度)为单位。设置texture
属性时,尺寸会自动设置为纹理的尺寸。
SpriteNode.texture
将Texture
用来绘制精灵。
如果值为None
,则使用其color
属性将子画面绘制为彩色矩形。否则,将使用纹理绘制精灵。
特效节点
class scene.EffectNode([position=(0, 0), z_position=0.0, scale=1.0, x_scale=1.0, y_scale=1.0, alpha=1.0, speed=1.0, parent=None])
EffectNode
对象可用于应用后期处理特效。
每次使用特效节点渲染新帧时,特效节点都遵循以下步骤:
- 特效节点将其子级绘制到专用帧缓冲区中。
- 它将使用标准混合模式之一将其专用帧缓冲区的内容混合到其父级的帧缓冲区中,类似于
SpriteNode
。你还可以使用自定义Shader
来渲染结果,以获得更高级的后期处理特效。
特效节点的属性
EffectNode.crop_rect
特效节点坐标系中的一个矩形,用于确定特效节点渲染的子级数量。
默认情况下,特效节点会根据其子级的累积帧自动确定其裁剪区域的大小和位置。在某些情况下,这可能效率不高,尤其是在子级的大小经常更改或某些子级不在屏幕上的情况下。
EffectNode.blend_mode
混合模式用于将过滤后的图像绘制到父级的帧缓冲区中。
混合模式中列出了此属性的可能值。默认值为BLEND_NORMAL
。
EffectNode.effects_enabled
设置为False
时,特效节点的渲染方式类似于常规Node
(忽略着色器,混合模式和裁剪矩形)。默认值为True
。
EffectNode.shader
当特效节点混合到父级的帧缓冲区中时调用的自定义着色器。
默认值为None
,这意味着将执行默认的混合行为。如果指定了着色器,则在将栅格化的图像混合到父级的帧缓冲区中时将调用该着色器。
标签节点
class scene.LabelNode(text, font=(‘Helvetica’, 20), args, *kwargs)
LabelNode
是SpriteNode
特殊的子类,它通过使用给定的字体将字符串渲染到图像中来自动生成纹理。设置LabelNode.text
或LabelNode.font
属性时,纹理将自动更新。
默认情况下,文本位于节点的中心位置。你可以通过调整Node.anchor_point
属性来更改此设置。
标签节点的属性
LabelNode.text
标签的字符串值。
LabelNode.font
标签的字体,为字体名称和字体大小(以磅为单位)的2元组。
形状节点
class scene.ShapeNode(path=None, fill_color=’white’, stroke_color=’clear’, shadow=None, args, *kwargs)
ShapeNode
是SpriteNode
呈现ui.Path
(矢量形状)的特殊子类。
形状具有可自定义的填充和描边颜色,可以选择使用阴影渲染。
形状节点的属性
ShapeNode.path
表示呈现形状的ui.Path
对象。ui.Path.line_width
属性确定形状轮廓(笔触)的宽度。
ShapeNode.fill_color
用于填充形状的颜色。
ShapeNode.stroke_color
用于形状轮廓的颜色。
ShapeNode.shadow
一个四元组(color, x_offset, y_offset, radius)
,用于指定可选的阴影。如果设置为None
,则不会渲染阴影。
其他类
场景视图
class scene.SceneView
SceneView
是ui.View
的子类,它绘制Scene
的内容并实现渲染循环。
通常,你不会显式创建SceneView
-调用run()
函数会自动执行此操作。如果以这种方式运行场景后需要修改视图的属性,则可以使用Scene.view
属性访问其视图。
出于调试目的,SceneView
将在底部的“状态行”中显示标准输出和异常,因此即使你的场景覆盖了整个屏幕,你也可以在开发过程中使用print
语句。
场景视图的属性
SceneView.scene
视图中当前呈现的场景。如果不设置此属性,则视图将为空。
SceneView.paused
将此设置为True
可暂停渲染循环。
SceneView.frame_interval
默认情况下,渲染循环每秒更新60次,这对应于帧间隔1。将其设置为较高的值会降低更新频率,例如,值为2则对应于每秒30帧。如果没有显式创建SceneView
,则还可以将此值作为参数传递给run()
函数。
SceneView.anti_alias
将此设置为True
可启用4x多重采样。这会产生(有时是很大的)性能开销,并且默认情况下处于禁用状态。
SceneView.shows_fps
设置为True
以启用显示当前帧速率的调试覆盖。
着色器
class scene.Shader(shader_src)
一个Shader
对象代表一个自定义OpenGL片段着色器,可用于修改SpriteNode
和EffectNode
对象的渲染行为(通过它们各自的shader
属性)。
Shader
编程是一个复杂的主题,完整的介绍超出了本文档的范围,但是基本概念确实非常简单。片段着色器本质上是直接在GPU上执行的功能/程序,负责为每个像素生成颜色值。着色器是使用GLSL(GL着色语言)编写的,它与C非常相似。
SpriteNode
基本的默认着色器仅使用其纹理坐标输入(自动设置)在其关联的Texture
中查找相应的颜色。一个简单的自定义着色器可以调整生成的颜色值(例如,将颜色转换为灰度),或修改纹理坐标以产生形态效果。
着色器有两种输入:Varying
和Uniform
。本质上,Uniform
的每个像素/片段具有相同的值,而Varying
是内插的。Varying
的一个例子是纹理坐标,每个像素显然都不同。Uniform
的示例是精灵的大小或当前时间戳。
呈现带有自定义着色器的SpriteNode
或EffectNode
时,会自动设置一些Varying
和Uniform
:
uniform float u_time
– 场景动画循环的当前时间戳uniform vec2 u_sprite_size
– 子画面的大小(以磅为单位)uniform float u_scale
– 屏幕的比例因子(对于视网膜屏幕通常为2.0)– 可用于转换u_sprite_size
为实际的屏幕像素。uniform sampler2D u_texture
– 子画面的纹理(对于EffectNode
,该纹理包含其子级的渲染)uniform vec4 u_tint_color
– 子画面的预乘颜色(对应于SpriteNode.color
属性)uniform vec4 u_fill_color
– 如果精灵没有纹理,则使用代替u_tint_color
。varying vec2 v_tex_coord
– 当前纹理(UV)坐标
即使这些Varying
和Uniform
是自动设置的,如果要使用它们,也必须在着色器中声明它们。
你也可以使用Shader.set_uniform()
方法声明和设置自定义Uniform
。自定义Uniform
可以是纹理(sampler2D),浮点和2-/3-/4-分量矢量(vec2/vec3/vec4)。
以下示例在Pythonista图标上产生了有趣的“涟漪”效果。它还演示了如何通过设置Uniform
来修改着色器行为–在这种情况下,波纹效果的中心会根据触摸事件而改变。
1 | from scene import * |
Shader.get_uniform(name)
返回具有给定名称的Uniform
的当前值。注意,均匀的类型必须是float
,vec2
,vec3
或vec4
(即取样器/纹理Uniform
不受此方法支持)。对于无效的Uniform
名称,将返回None
。
Shader.set_uniform(name, value)
将具有给定名称的Uniform
设置为新值。该值可以是用于单个数字float
或int
的Uniform
,数字类型的Uniform
的序列类型是vec2
,vec3
或者vec4
,或Texture
用于对象sampler2D
的Uniform
。不支持其他类型的Uniform
。
动作
class scene.Action
Action
是的一个动画,通常改变Node
的属性随时间变化。可以使用其Node.run_action()
方法将其添加到节点。
不同类型的动作(对于不同Node
属性)使用不同类的方法创建的,例如Action.move_to()
,Action.rotate_by()
等
将动作添加到Node
后,便无法再对其进行更改,但是你可以使用Node.remove_action()
或Node.remove_all_actions()
将其删除(并停止播放动画)。
一些操作会修改其他操作的行为:
一个Action.sequence()
有多个子行为。序列中的每个动作在上一个动作结束后开始。
一个Action.group()
有多个子行为。该组中存储的所有操作都将同时开始执行。完成所有动作后,整个小组结束。与Action.sequence()
结合使用时特别有用。
一个Action.repeat()
动作存储单个子动作。子操作完成后,将重新启动。
组,序列和重复动作可以嵌套。将动作组合在一起的能力使你可以向节点添加非常复杂的行为。
动画动作的默认持续时间为0.5秒。
classmethod Action.call(func[, duration])
创建一个调用函数(或其他可调用对象)的自定义操作。
如果没有传递持续时间参数,则该函数仅被调用一次,并且不能接受任何参数。
如果使用了duration
参数,则将在其上执行操作的节点以及当前进度(介于0.0和1.0之间)传递给该函数,并且它必须具有以下声明:
1 | def custom_action(node, progress): |
classmethod Action.fade_by(alpha[, duration, timing_mode])
创建一个操作,以相对值调整节点的alpha
值。
执行动作时,Node.alpha
属性会设置为新值。
classmethod Action.fade_to(alpha[, duration, timing_mode])
创建一个将节点的alpha
值调整为新值的操作。
执行动作时,Node.alpha
属性会设置为新值。
classmethod Action.group(actions…)
创建一个可并行运行一组动作的动作。
当动作执行时,组成该组的动作将立即开始并并行运行。小组动作的持续时间是动作集合中最长的持续时间。如果组中某个动作的持续时间小于该组的持续时间,则该动作完成,然后闲置,直到该组完成其余动作。在创建重复组的重复动作时,这一点最为重要。
你可以将组的操作作为单独的参数或单个序列参数(例如,列表)传递。
classmethod Action.move_by(dx, dy[, duration, timing_mode])
创建一个使节点相对于其当前位置移动的动作。
classmethod Action.move_to(x, y[, duration, timing_mode])
创建将节点移动到新位置的动作。
classmethod Action.repeat(action, repeat_count)
创建一个动作,该动作将指定另一个动作重复的次数。如果repeat_count
为<= 0,则操作将重复一次(或直到将其显式删除为止)。
classmethod Action.repeat_forever(action)
创建一个动作,该动作无限期地重复另一个动作。
classmethod Action.rotate_by(radians[, duration, timing_mode])
创建一个将节点旋转一个相对值的动作。
classmethod Action.rotate_to(radians[, duration, timing_mode])
创建一个动作,该动作将节点逆时针旋转到绝对角度。
classmethod Action.scale_by(scale[, duration, timing_mode])
创建一个动作,以相对值更改节点的x和y比例值。
classmethod Action.scale_to(scale[, duration, timing_mode])
创建一个更改节点的x和y比例值的动作。
classmethod Action.scale_x_to(scale[, duration, timing_mode])
创建一个更改节点的x比例值的动作。
classmethod Action.scale_y_to(scale[, duration, timing_mode])
创建一个更改节点的y比例值的动作。
classmethod Action.set_uniform(name, value[, duration, timing_mode])
创建一个动作,将给定的着色器动画统一为新值。请注意,此操作仅对SpriteNode
和EffectNode
对象有效。该值必须是单数(float
的Uniform
)或2-4数字的序列(vec2
,vec3
和vec4
的Uniform
)。
classmethod Action.sequence(actions…)
创建一个可按顺序运行一系列操作的操作。
执行动作时,序列中的第一个动作开始并运行到完成。序列中的后续动作以类似的方式运行,直到序列中的所有动作都已执行。序列动作的持续时间是序列中动作的持续时间之和。
你可以将序列的动作作为单独的参数或单个序列参数(例如列表)传递。
classmethod Action.wait(wait_duration)
创建一个在指定时间段内处于空闲状态的操作。
执行动作时,动作将等待指定的时间,然后结束。这通常用作一系列动作的一部分,以便在其他两个动作之间插入延迟。
动作的属性
Action.duration
动作持续时间,以秒为单位。对于某些类型的动作(例如组,序列),持续时间将被忽略。请注意,将动作添加到Node
后,更改其持续时间(或其他属性)无效。
Action.timing_mode
用于执行动作的计时模式。
在计时模式下列出了此属性的可能值。默认值为TIMING_LINEAR
。
对于某些类型的动作(例如,组,序列),计时模式将被忽略。请注意,将动作添加到Node
后,更改其计时模式(或其他属性)无效。
纹理
class scene.Texture(image)
Texture
对象被SpriteNode
对象用来渲染其内容。纹理是已加载到GPU内存中的图像。可以使用内置图像的名称(字符串)或ui.Image
对象来初始化纹理。
Texture.subtexture(rect)
从现有纹理中的矩形区域创建新纹理。返回的纹理对象与原始纹理对象共享相同的纹理数据,这意味着只有一个副本的纹理数据保留在内存中。
的矩形参数描述纹理的一部分在单元坐标,例如将产生从原始纹理的左下象限的纹理。(0, 0, 0.5, 0.5)
此方法可用于精灵表/纹理图集。
Texture.filtering_mode
用于缩放纹理的过滤模式。可以是纹理过滤模式下列出的常数之一(默认情况下为FILTERING_LINEAR
)。
Texture.size
纹理的大小(以像素为单位)。请注意,这没有考虑屏幕的比例因子。
触摸
class scene.Touch(x, y, prev_x, prev_y, touch_id)
这个类的实例传递给Scene.touch_began()
,Scene.touch_moved()
和Scene.touch_ended()
方法。Scene
对象也具有touches
属性(将touch_id
映射到Touch
对象的字典)。
Touch.location
触摸作为Point
对象的当前位置。
Touch.prev_location
触摸的先前位置(在移动之前),为Point
对象。
几何类型
二维向量
class scene.Vector2(x, y)
Vector2
类是Point
和Size
的基础类。
向量(以及随后的点和大小)支持加法(+
),减法(-
),乘法(*
)和除法(/
)运算符。两个向量都支持加法和减法。乘法和除法也可以应用于向量和标量(数字)。
将向量传递给内置abs()
函数可计算向量的长度。与减法结合使用时,这尤其有用,例如,非常容易计算两个点之间的距离。abs(p2 - p1)
也可以使用in
运算符确定点是否位于Rect
内。
在大多数情况下,Vector2
其行为类似于序列,类似于2元组。例如,你可以选择使用下标符号(v[0]
)访问其x
组件。向量还支持迭代,参数解压缩等。
每当一个Vector2
,Point
或Size
对象被用作在scene
模块的属性,可以替代地提供4个数字(例如列表或元组)的任何序列。
点
class scene.Point(x, y)
Vector2
的子类,用于表示位置。此类不提供任何其他方法或属性。
尺寸
class scene.Size(w, h)
Vector2
的子类,用于表示尺寸。与Vector2
唯一的区别是x
和y
分量可以使用Size.w(width)
和Size.h(height)
进行访问。
矩形
class scene.Rect(x, y, w, h)
Rect
类用于边界矩形和其他矩形值,例如,Node.frame
属性。矩形表示为(x,y,w [idth],h [eight])
,其中(x,y)
是其左下角。
在大多数情况下,Rect
行为类似于序列,类似于4元组。例如,你可以选择使用下标符号(r[0]
)访问其x
组件。矩形还支持迭代,参数解压缩等。
每当将Rect
对象用作scene
模块中的属性时,都可以选择提供4个数字的任何序列(例如,列表或元组)。
Rect.x
矩形左下角的x
分量。
Rect.y
矩形左下角的y
分量。
Rect.w
Rect.width
矩形的宽度。
Rect.h
Rect.height
矩形的高度。
Rect.origin
相当于 Point(rect.x, rect.y)
Rect.size
相当于 Size(rect.w, rect.h)
Rect.min_x
等效于(左边缘的x
分量)min(rect.x, rect.x + rect.w)
Rect.max_x
等效于(右边缘的x
分量)max(rect.x, rect.x + rect.w)
Rect.min_y
等效于(底边的y
分量)min(rect.y, rect.y + rect.h)
Rect.max_y
等效于(上边缘的y
分量)max(rect.y, rect.y + rect.h)
Rect.center([p])
不带参数调用时,返回矩形的中心。当将Point
作为参数传递时,将调整矩形的x
和y
值,以便矩形的新中心为p
。
Rect.contains_point(p)
如果给定的点位于矩形的边界内,则返回True
,否则返回False
。
Rect.contains_rect(other_rect)
如果给定的矩形完全位于此矩形的边界内,则返回True
,否则返回False
。
Rect.intersects(other_rect)
如果此矩形与另一个矩形相交,则返回True
,否则返回False
。
Rect.intersection(other_rect)
返回该矩形与另一个矩形的交点相对应的Rect
。
Rect.union(other_rect)
返回包含两个矩形的最小Rect
。
Rect.translate(x, y)
相当于 Rect(r.x + x, r.y + y, r.w, r.h)
Rect.inset(top, left[, bottom, right])
返回一个由给定的边缘插图调整的矩形。bottom / right
是可选的,默认为与top / left
相同的值。
函数
scene.gravity()
返回当前重力矢量(x,y,z)
–每个分量值将在0.0到1.0之间。这可用于确定设备的当前方向,通常用于运动控制游戏。
scene.get_screen_size()
返回屏幕尺寸(以磅为单位)作为Size
对象。请注意,该值取决于方向-你可能希望使用min(get_screen_size())
来获取任何方向上的最小尺寸。
scene.get_screen_scale()
返回当前设备屏幕的比例因子。对于视网膜屏幕,通常为2.0或3.0,对于非视网膜屏幕通常为1.0。
scene.get_image_path(name)
返回与内置图像名称相对应的文件的绝对路径。
scene.run(scene, orientation=DEFAULT_ORIENTATION, frame_interval=1, anti_alias=False, show_fps=False, multi_touch=True)
运行给定的Scene
对象。默认情况下,场景以当前设备方向运行,将参数设置为PORTRAIT
或LANDSCAPE
以强制特定的方向(并禁止自动旋转)。重要:方向参数对从iOS 10开始的iPad无效,因为从技术上讲,不可能在支持分屏多任务的应用程序中锁定方向。
默认情况下,场景的update()
方法每秒调用60次。将frame_interval
参数设置为2表示30fps,3表示20等等。
scene.get_controllers()
以字典列表的形式返回所有已连接的MFi游戏控制器的当前状态(另请参阅游戏控制器)。
常量
方向
以下常量用于函数的方向参数run()
。
scene.DEFAULT_ORIENTATION
以当前设备方向开始场景,允许自动旋转。
scene.PORTRAIT
强制纵向。
scene.LANDSCAPE
强制横向。
混合模式
scene.BLEND_NORMAL
通过将源alpha
值相乘来混合源颜色和目标颜色。
scene.BLEND_ADD
源颜色和目标颜色被添加在一起。
scene.BLEND_MULTIPLY
源颜色乘以目标颜色。
纹理过滤模式
以下常量可用于该Texture.filtering_mode
属性。
scene.FILTERING_LINEAR
线性插值,默认过滤模式
scene.FILTERING_NEAREST
最近邻插值–这是像素艺术的推荐模式,因为在按比例缩放低分辨率的艺术作品时会产生尖锐的边缘。
计时模式
以下常量可用于Action
对象的timing_mode
属性,以确定其插值函数。
scene.TIMING_LINEAR
简单线性插值(默认值)。
scene.TIMING_EASE_IN
缓插
scene.TIMING_EASE_IN_2
替代性轻松插入(二次)
scene.TIMING_EASE_OUT
缓和插值
scene.TIMING_EASE_OUT_2
替代缓和插值(二次)
scene.TIMING_EASE_IN_OUT
缓入缓出插值。
scene.TIMING_SINODIAL
与TIMING_EASE_IN_OUT
相似,但宽松阶段略显不足。
scene.TIMING_ELASTIC_IN
动画开始时的“橡皮筋”效果。
scene.TIMING_ELASTIC_OUT
动画结尾处的“橡皮筋”效果。
scene.TIMING_ELASTIC_IN_OUT
动画两端的“橡皮筋”效果。
scene.TIMING_BOUNCE_IN
动画开始时的“弹跳”效果。
scene.TIMING_BOUNCE_OUT
动画末尾的“弹跳”效果。
scene.TIMING_BOUNCE_IN_OUT
动画两端的“弹跳”效果。
scene.TIMING_EASE_BACK_IN
超调动画,从一开始就缓解了。
scene.TIMING_EASE_BACK_OUT
超调动画,最后缓解。
scene.TIMING_EASE_BACK_IN_OUT
超调动画,在开始和结束时都得到了缓解。