scene — 2D Games and Animations#
The scene module provides an easy way to create hardware-accelerated 2D graphics and animations, specifically games.
Overview#
Introduction#
For every scene-based project, you first have to create a subclass of Scene that is responsible for drawing your content, responding to touch events etc. To actually get a scene on screen, you instantiate your subclass, and pass it to the run() function. The Scene class provides various methods that you can override to customize its behavior, for example Scene.setup(), which is called just before your scene becomes visible on screen:
from scene import *
class MyScene (Scene):
def setup(self):
self.background_color = 'green'
run(MyScene())
In the minimal example above, the Scene.setup() method simply sets the background color of the scene to green.
To add actual content to your scene, you typically create Node objects – there are a couple of different subclasses of Node corresponding to different content, e.g. a SpriteNode renders an image, a LabelNode text, etc. A Scene is also a subclass of Node, and nodes can contain other nodes (forming a tree or “scene graph”). This is often useful to move or rotate a group of objects as a single entity. All nodes have position, rotation, scale, and alpha (opacity) attributes that determine how the node and its children are drawn. The default position is (0, 0) which corresponds to the lower-left corner of the screen.
In the following example, the setup() method is extended to add a space ship at the center of the screen:
from scene import *
class MyScene (Scene):
def setup(self):
self.background_color = 'midnightblue'
self.ship = SpriteNode('spc:PlayerShip1Orange')
self.ship.position = self.size / 2
self.add_child(self.ship)
run(MyScene())
A couple of things to note here:
The string that was passed to the
SpriteNodeinitializer is the name of a built-in image. You can access built-in images in Pythonista using the [+] button at the top of the editor.For convenience, the objects representing sizes and positions in the
scenemodule support a couple of standard operators – in this case, the scene’s size (which is set automatically beforesetup()is called) is simply divided by two, resulting in the center of the screen. Sizes and points can be used interchangeably, so assigning a size to thepositionattribute works. You could also use a simple tuple of two numbers.By default, the
positionattribute of aSpriteNodecorresponds to its center. In some cases it may be more convenient to set the position of one of the sprite’s corners instead – you can use theanchor_pointattribute to change this behavior.
Touches and Simple Animations#
Let’s extend this example once more to make it respond to touches. To do this, we simply override the Scene’s touch_began() method:
from scene import *
class MyScene (Scene):
def setup(self):
self.background_color = 'midnightblue'
self.ship = SpriteNode('spc:PlayerShip1Orange')
self.ship.position = self.size / 2
self.add_child(self.ship)
def touch_began(self, touch):
x, y = touch.location
move_action = Action.move_to(x, y, 0.7, TIMING_SINODIAL)
self.ship.run_action(move_action)
run(MyScene())
This last example introduces the Action class. Actions allow you to easily animate a Node’s attributes, in this case its position. The timing mode (TIMING_SINODIAL) specifies the type of interpolation between the start and end values. If you leave out this argument, you get the default (linear) interpolation which results in a more abrupt movement. There are a lot of other Action factory methods for rotating or scaling nodes, changing their opacity (alpha), or even removing them from their parent node. You can also combine multiple Actions using Action.group() and Action.sequence(), or create an action that repeats another action using Action.repeat().
Similar to touch_began(), there are methods for detecting when a touch moves or ends – touch_moved() and touch_ended().
Advanced Animation and Motion Control#
While Actions provide an easy-to-use high-level API for animating a scene’s content, you can also animate changes on a frame-by-frame basis by overriding the Scene’s update() method. The following example demonstrates this by moving the space ship in response to the device’s orientation (using the gravity() function). By default, the update() method is called 60 times per second, so the ship’s position is set directly, instead of relying on Action-based animations:
from scene import *
import sound
class MyScene (Scene):
def setup(self):
self.background_color = 'midnightblue'
self.ship = SpriteNode('spc:PlayerShip1Orange')
self.ship.position = self.size / 2
self.add_child(self.ship)
def update(self):
x, y, z = gravity()
pos = self.ship.position
pos += (x * 15, y * 15)
# Don't allow the ship to move beyond the screen bounds:
pos.x = max(0, min(self.size.w, pos.x))
pos.y = max(0, min(self.size.h, pos.y))
self.ship.position = pos
def touch_began(self, touch):
laser = SpriteNode('spc:LaserBlue9', position=self.ship.position, z_position=-1, parent=self)
laser.run_action(Action.sequence(Action.move_by(0, 1000), Action.remove()))
sound.play_effect('arcade:Laser_1')
run(MyScene(), PORTRAIT)
The gravity() function used here provides a very easy-to-use method of determining the device’s orientation. For more fine-grained control, you may want to use the motion module instead. In motion-controlled games, auto-rotation can be annoying, so this scene is restricted to portrait orientation using an additional argument to the run() function.
As a bonus, the space ship now shoots a laser when you touch the screen. The z_position attribute that is set when initializing the laser sprite determines the drawing order – it is set to -1 here so that the laser doesn’t appear on top of the ship. After the laser is added to the scene, it runs a sequence of two actions: The first one moves it by 1000 points upwards, and then it’s removed from the scene using the special remove action (which is not really an animation, but provided as an Action so that it can be part of an animation sequence).
This was just a very basic overview – there’s a lot more you can do with the scene module. The included Examples folder contains a couple of complete games that you can use to learn more advanced techniques.
Geometry#
The coordinate system of a scene has its origin (0, 0) in the bottom-left corner.
As briefly mentioned in the overview, the scene module provides a couple of convenient classes for representing 2D geometry, primarily Rect, Point and Size. The latter two are basically identical, but used in different contexts.
These geometry objects are used for various Node attributes, e.g. Node.position, SpriteNode.size, Node.frame etc. When you assign a new value to these attributes, you don’t have to create a Point or Size object explicitly though – any sequence of two numbers (e.g. a tuple or list) will do just as well.
All of these classes behave like sequences, so you can e.g. access a Point’s x coordinate using point[0] (though it’s typically more convenient to use point.x).
For Vector2, Point and Size, a couple of math operators are supported for convenience:
Multiplying a vector with a scalar (i.e. a number) multiplies both components with that number. The same goes for division. For example,
Size(100, 200) * 0.5results inSize(50, 100).Multiplying a vector with another vector does component-wise multiplication, e.g.
Point(100, 100) * (2, 3)results inPoint(200, 300).Adding a vector to another vector behaves the same way, e.g.
Point(100, 100) + (20, 50)results inPoint(120, 150).
For hit-testing purposes, you can check if a Point lies within a Rect using the in operator. To check if one rectangle intersects with another, you can use the Rect.intersects() method (or Rect.intersection() if you need to know exactly where the rectangles intersect).
Colors#
Node attributes that represent a color, e.g. SpriteNode.color or Scene.background_color can be set in a variety of ways:
As an HTML hex color string, e.g.
'#ff0000'for red.As a CSS color name, e.g.
'green','blue','magenta'…As a tuple of 3 or 4 numbers, corresponding to the red, green, blue, and alpha components in the range 0.0 - 1.0. The alpha component defaults to 1.0, i.e. fully opaque.
As a single number between 0.0 and 1.0, representing a grayscale value (0.0 = black, 1.0 = white).
Regardless of how you set a color attribute, reading it back will always result in a 4-tuple (r, g, b, a).
Classic Render Loop#
In addition to the node-based approach described above, the scene module also supports a classic rendering loop that was the primary interface in previous versions of Pythonista. In most cases, a node-based scene will result in significantly better performance, but the classic rendering loop may be easier to understand if you already know similar programming environments, e.g. Processing.
To use this mode, you also have to create a subclass of Scene, but instead of adding nodes to it, you simply override the draw() method. This method is called for every frame (i.e. 60 times per second), and you can use module-level drawing functions to “paint” your content in every frame. Images and shapes that you draw to the screen in this way are not preserved from one frame to the next – in every call of the draw() method, you basically start with a blank screen.
Similar to classic OpenGL, you set up global state variables (like fill color and coordinate transformations) before calling the actual drawing functions. The following example draws a couple of rotated rectangles:
from scene import *
class MyScene (Scene):
def draw(self):
background('gray')
colors = ['red', 'green', 'blue', 'yellow']
# Move to the center of the screen:
translate(self.size.w/2, self.size.h/2)
for color in colors:
# Set the current fill color (this affects the drawing commands that follow)
fill(color)
# Rotate the transformation matrix (this is cumulative):
rotate(-20)
# Note: The coordinates are relative to the center of the screen because of the previous `translate` command:
rect(-50, -50, 100, 100)
run(MyScene())
If you’ve used a previous version of Pythonista, you may also be familiar with the Layer and Animation classes that were conceptually similar to Node and Action. These classes are still available for backwards compatibility, but it is strongly recommended to use the new Node/Action APIs instead because they provide significantly better performance.
Note
The functions you can use for drawing in this mode are documented in the scene_drawing module, though you don’t have to import scene_drawing explicitly if you want to use them.
Integration with the ui Module#
The easiest way to run a scene is to use the run() function, which presents the scene in full-screen. Sometimes this is not what you want, especially if the scene you’re building is not actually a game. For more flexibility, you can also create a SceneView explicitly, set its scene attribute to the scene you want to present, and then add it to a view hierarchy that you created using the ui module.
You can also use this approach to add traditional UI elements (e.g. text fields) to your game. For this purpose, you don’t have to create a SceneView – you can simply use the view that was created automatically, using your Scene’s view attribute (this will only work if a scene is already being presented of course).
The ui module can also be useful for rendering vector images (shapes, text etc.) that you want to use as textures. A Texture can be initialized from a ui.Image for this purpose, typically one that you create using an ui.ImageContext. The ShapeNode and LabelNode classes use this approach to render their content.
Game Controllers#
The scene module has support for controlling games using MFi game controllers, like the Nimbus SteelSeries gamepad.
Supporting hardware controller input in your game is quite simple – you basically just have to override the Scene.controller_changed() method in your Scene subclass to respond to controller events (e.g. a button being pressed/released). The method gets two parameters: A key, which is a string that represents the element of the controller that changed (for example 'button_a'), and the current value of the controller element. The type of the value depends on the type of controller element – for basic buttons, it’s a boolean, for pressure-sensitive triggers a floating-point value between 0.0 and 1.0, and for dpads and thumbsticks, it’s a Point object (with x and y values in the range -1.0 to +1.0).
In some cases, it can be more convenient to query the current state of the controller(s) in the Scene.update() method or elsewhere. To do this, you can use the module-level get_controllers() function. This will return a list of dictionaries that represent the current state of all buttons, the dpad, thumbsticks, etc. for every controller that is currently connected.
Node Classes#
Scene#
- class scene.Scene#
A scene is the root node in a tree of nodes (
Nodeobjects). These nodes provide content that the scene animates and renders for display. To display a scene, you typically call the module-levelrun()function to present it in full-screen. You can also create aSceneViewexplicitly, if you want to display a scene within other UI content.A scene calculates the contents of a new frame by processing the following actions in order:
The scene calls its
update()method.The scene executes actions on its children.
The scene calls its
did_evaluate_actions()method.The scene renders all of its nodes and updates the view to display the new contents.
You typically create at least one subclass of
Sceneto handle touch events (by overridingtouch_began(),touch_moved(),touch_ended()) and any other interaction with your game’s content.Note that
Sceneis a subclass ofEffectNode, so you can use a customShaderfor full-scene post-processing effects. Unlike a vanillaEffectNodehowever, a scene’seffects_enabledattribute is set to False by default.
- Scene.setup()#
This gets called once, just before the scene is presented on screen. You can use this to set up your scene. The
sizeandboundsattributes are already valid at this point, so you can use them to determine the layout of your content.
- Scene.touch_began(touch)#
This method is called when a touch begins on the scene’s view. You typically don’t call this directly, but implement it as part of your
Scenesubclass.You can inspect the touch object’s location attribute to get the position of the touch in the scene’s coordinate system. To distinguish between simultaneous touches, you can use the touch’s touch_id attribute.
- Scene.touch_moved(node, touch)#
This method is called when a touch moves in the scene’s view. You typically don’t call this directly, but implement it as part of your
Scenesubclass.You can inspect the touch object’s location attribute to get the position of the touch in the scene’s coordinate system. To distinguish between simultaneous touches, you can use the touch’s touch_id attribute.
- Scene.touch_ended(node, touch)#
This method is called when a touch ends in the scene’s view. You typically don’t call this directly, but implement it as part of your
Scenesubclass.You can inspect the touch object’s location attribute to get the position of the touch in the scene’s coordinate system. To distinguish between simultaneous touches, you can use the touch’s touch_id attribute.
- Scene.did_change_size()#
This method gets called whenever the scene’s size changes, usually when the screen rotates. You typically override this to reposition your content. The
sizeattribute of the scene is already set to the new size when this is called.
- Scene.did_evaluate_actions()#
This method gets called after a scene has finished processing the actions of its child nodes.
- Scene.update()#
Performs any scene-specific updates that need to occur before scene actions are evaluated.
Do not call this method directly; it is called exactly once per frame, so long as the scene is presented in a view and is not paused. By default, this method does nothing. Your scene subclass should override this method and perform any necessary updates to the scene.
- Scene.pause()#
Gets called automatically when the home button is pressed while a scene is running. You can override this to save persistent state for example. The default implementation does nothing.
- Scene.resume()#
Gets called automatically when a scene is resumed (after being sent to the background with the home button). The default implementation does nothing.
- Scene.stop()#
Gets called automatically when a scene is stopped (by tapping the “x” button). You can override this to save persistent state. The default implementation does nothing.
- Scene.present_modal_scene(other_scene)#
Present another scene on top of this one. This can be useful for overlay menus etc. While the scene is being presented, it receives all touch events.
- Scene.dismiss_modal_scene()#
Close a scene that is being presented modally using
Scene.present_modal_scene().
- Scene.controller_changed(key, value)#
This method gets called automatically when the state of any connected game controller changes, for example, when a button is pressed/released, or when the direction of one of the thumbsticks or the dpad changes.
The
keyparameter is a string that specifies the element of the controller that changed (for example'button_a','dpad','thumbstick_left'…),valuecontains the current value of that element. The type ofvaluevaries with the type of controller element. For directional input elements (dpads, thumbsticks), the value is aPointobject. For others, it’s either a float (when the element is pressure-sensitive) or a boolean.
Scene Attributes#
- Scene.dt#
The time (in seconds) that has passed since the last invocation of
update(). You can use this to calculate the progress of custom animations.
- Scene.size#
The size of the entire drawable area.
- Scene.t#
The time (in seconds) that has passed since the scene was started. You can use this to calculate the progress of custom animations.
- Scene.touches#
A dictionary of all touches that are currently active. The keys correspond to the
touch_idattribute of theTouchobjects.
- Scene.background_color#
The background color of the scene.
The default value is dark gray.
- Scene.size#
The dimensions of the scene in points (read-only). This corresponds to the size of the scene’s view.
- Scene.view#
The
Viewthat is currently presenting the scene. May be None if the scene is not currently being presented. (read-only)
- Scene.presented_scene#
The scene that is currently being presented using
Scene.present_modal_scene()(if any, None otherwise).
- Scene.presenting_scene#
The presenting scene, if this scene is being presented by another scene (using
present_modal_scene()).
Node#
- 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])#
The
Nodeclass is the fundamental building block of a scene. The basicNodeclass doesn’t draw anything by itself – its primary role is to provide baseline behavior that its subclasses use. Nodes can also contain other nodes to modify them as a group. For drawing actual content, you will typically use one ofNode’s subclasses:SpriteNode– A node that draws a textured spriteLabelNode– A specializedSpriteNodethat renders a text string.ShapeNode– A specializedSpriteNodethat renders a shape based on a bezier path (e.g. a circle or rounded rectangle).EffectNode– A node that applies effects to its children using a custom shader.
Nodes are organized hierarchically into node trees, similar to how views and subviews work. Most commonly, a node tree is defined with a
Scenenode as the root node and other content nodes as descendants. The scene node runs an animation loop that processes actions on the nodes, and then renders the contents of the node tree for display.Every node in a node tree provides a coordinate system to its children. After a child is added to the node tree, it is positioned inside its parent’s coordinate system by setting its position attribute. A node’s coordinate system can be scaled and rotated by changing its
x_scale,y_scale, androtationattributes. When a node’s coordinate system is scaled or rotated, this transformation is applied both to the node’s own content and to that of its descendants.The
Nodeclass does not perform any drawing of its own. However, many subclasses render visual content and so theNodeclass understands some visual concepts:The
frameattribute provides the bounding rectangle for a node’s visual content, modified by thescaleandrotationattributes. The frame is non-empty if the node’s class draws content. Each node subclass determines the size of this content differently. In some subclasses, the size of the node’s content is declared explicitly, such as in theSpriteNodeclass. In other subclasses, the content size is calculated implicitly by the class using other object properties. For example, aLabelNodeobject determines its content size using the label’s text and font characteristics.A node’s
bboxis the largest rectangle that includes the frame of the node and the frames of all its descendants.Other attributes, such as the
alphaattribute, affect how the node and its descendants are drawn.Any node in the tree may run actions (
Actionobjects), which are used to animate the properties of a node, e.g. to move it to a new position smoothly.
- Node.add_child(node)#
Adds a node to the end of the receiver’s list of child nodes.
- Node.remove_from_parent()#
Removes the node from its parent node.
- Node.remove_action(key)#
Ends and removes a specific action, identified by its key, from the node. The key is an arbitrary string that was passed to the
Node.run_action()method.
- Node.remove_all_actions()#
Ends and removes all actions from the node.
When an action is removed from the node, any remaining animation the action would perform is skipped; however, previous changes are not reverted.
- Node.render_to_texture([crop_rect])#
Creates a
Textureobject by rendering a snapshot of this node and its children.
- Node.point_to_scene(point)#
Convert a point from this node’s coordinate system to the coordinate system of its containing scene. If the node is not part of a scene, a
ValueErroris raised.
- Node.point_from_scene(point)#
Convert a point from the coordinate system of this node’s scene to the local coordinate system of the node. If the node is not part of a scene, a
ValueErroris raised.
- Node.run_action(action[, key])#
Adds an action to the list of actions executed by the node.
If an action using the same key (an arbitrary string) is already running, it is removed before the new action is added.
Node Attributes#
- Node.bbox#
Calculates a rectangle in the parent’s coordinate system that contains the content of the node and all of its descendants.
- Node.alpha#
The transparency value applied to the node’s contents.
The
Nodeclass does not perform drawing, but many of its subclasses do. When a node or any of its descendants are drawn, the alpha component of each pixel is multiplied by the node’s alpha property and then clamped to the range 0.0-1.0. This modified alpha value is used to blend the pixel into the framebuffer. Subclasses that render content define properties that determine the blending operations used in conjunction with the alpha value to blend pixels into the parent’s framebuffer.
- Node.frame#
A rectangle in the parent’s coordinate system that contains the node’s content, ignoring the node’s children. (read-only)
- Node.children#
A list of this node’s child nodes. Note that modifying this list has no effect – use
Node.add_child()andNode.remove_from_parent()instead.
- Node.parent#
The node’s parent node. (read-only)
- Node.paused#
A Boolean value that determines whether actions on the node and its descendants are processed.
- Node.position#
The position (x, y) of the node in its parent’s coordinate system.
- Node.scene#
The scene node that contains the node. (read-only)
If the node is not embedded in a scene, the value is None.
- Node.speed#
A speed modifier applied to all actions executed by a node and its descendants.
- Node.x_scale#
A scaling factor that multiplies the width of a node and its children.
The
x_scaleattribute scales the width of the node and all of its descendants. The scale value affects how a node’s frame is calculated, its hit test area, how it is drawn, and other similar characteristics. The default value is 1.0.
- Node.y_scale#
A scaling factor that multiplies the height of a node and its children.
The
y_scaleattribute scales the height of the node and all of its descendants. The scale value affects how a node’s frame is calculated, its hit test area, how it is drawn, and other similar characteristics. The default value is 1.0.
- Node.z_position#
The z position of a node determines the order in which it’s drawn, relative to its siblings. The default value is 0.0; nodes with larger values are rendered in front of nodes with smaller values.
- Node.rotation#
The node’s rotation about the z axis (in radians).
The default value is 0.0, which indicates no rotation. A positive value indicates a counterclockwise rotation. When the coordinate system is rotated, it affects the node and its descendants.
SpriteNode#
- 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])#
A
SpriteNodeis a node that draws a textured image, a colored square, or a textured image blended with a color. You can also provide a custom shader to create your own rendering effects.When initializing a
SpriteNode, you can provide the texture as either aTextureobject, or the name of a built-in image or image file (a string).
SpriteNode Attributes#
- SpriteNode.anchor_point#
Defines the point in the sprite that corresponds to the node’s position.
You specify the value for this property in the unit coordinate space. The default value is (0.5, 0.5), which means that the sprite is centered on its position.
- SpriteNode.blend_mode#
The blend mode used to draw the sprite into the parent’s framebuffer.
The possible values for this property are listed in Blend Modes. The default value is
BLEND_NORMAL.
- SpriteNode.color#
The sprite’s color.
If the
textureattribute is set, the image is tinted with the given color. If thetextureattribute is None, the color is used to draw a colored rectangle.
- SpriteNode.shader#
A property that determines whether the sprite is rendered using a custom shader.
The default value is None, which means that the normal behavior for sprite rendering is performed. If a
Shaderis attached to this attribute, the custom shader is used to render the sprite.
EffectNode#
- 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])#
An EffectNode object can be used to apply post-processing effects.
Each time a new frame is rendered using the effect node, the effect node follows these steps:
The effect node draws its children into a private framebuffer.
It blends the contents of its private framebuffer into its parent’s framebuffer, using one of the standard blend modes, similar to a
SpriteNode. You can also use a customShaderto render the result for more advanced post-processing effects.
EffectNode Attributes#
- EffectNode.crop_rect#
A rectangle in the effect node’s coordinate system that determines how much of its children the effect node renders.
By default, an effect node automatically determines the size and position of its crop rect based on the accumulated frames of its children. In some cases, this may be inefficient, especially if the size of the children changes frequently, or if some of the children are off-screen.
- EffectNode.blend_mode#
The blend mode used to draw the filtered image into the parent’s framebuffer.
The possible values for this property are listed in Blend Modes. The default value is
BLEND_NORMAL.
- EffectNode.effects_enabled#
When set to False, the effect node renders like a regular
Node(shader, blend mode, and crop rect are ignored). The default is True.
- EffectNode.shader#
A custom shader that is called when the effect node is blended into the parent’s framebuffer.
The default value is None, meaning that default blending behavior executes. If a shader is specified, it is called when the rasterized image is blended into the parent’s framebuffer.
LabelNode#
- class scene.LabelNode(text, font=('Helvetica', 20), *args, **kwargs)#
A
LabelNodeis a specialized subclass ofSpriteNodethat automatically generates a texture by rendering a string into an image, using the given font. When you set theLabelNode.textorLabelNode.fontattributes, the texture is updated automatically.By default, the text is centered on the node’s
position. You can change this by adjusting theNode.anchor_pointattribute.
LabelNode Attributes#
- LabelNode.text#
The string value of the label.
- LabelNode.font#
The font of the label, as a 2-tuple of font name and font size (in points).
ShapeNode#
- class scene.ShapeNode(path=None, fill_color='white', stroke_color='clear', shadow=None, *args, **kwargs)#
A
ShapeNodeis a specialized subclass ofSpriteNodethat renders aui.Path(a vector shape).The shape has customizable fill and stroke colors, and can optionally be rendered with a drop shadow.
ShapeNode Attributes#
- ShapeNode.path#
A
ui.Pathobject that represents the shape to be rendered. Theui.Path.line_widthattribute determines the width of the shape’s outline (stroke).
- ShapeNode.fill_color#
The color that is used to fill the shape.
- ShapeNode.stroke_color#
The color that is used for the shape’s outline.
- ShapeNode.shadow#
A 4-tuple of (color, x_offset, y_offset, radius) that specifies an optional drop shadow. If set to None, no drop shadow is rendered.
Other Classes#
SceneView#
- class scene.SceneView#
SceneViewis a subclass ofui.Viewthat draws aScene’s content and implements a rendering loop.Typically, you will not create a
SceneViewexplicitly – calling therun()function does this automatically. If you need to modify the view’s attributes after running your scene this way, you can access its view using theScene.viewattribute.For debugging purposes, a
SceneViewwill show standard output and exceptions in a “status line” at the bottom, so you can use print statements during development, even if your scene covers the whole screen.
SceneView Attributes#
- SceneView.scene#
The scene that is currently presented in the view. Without setting this attribute, the view will be empty.
- SceneView.paused#
Set this to True to pause the rendering loop.
- SceneView.frame_interval#
By default, the rendering loop updates 60 times per second, which corresponds to a frame interval of 1. Setting this to a higher value reduces the update frequency, e.g. a value of 2 corresponds to 30 frames per second. If you don’t create a
SceneViewexplicitly, you can also pass this value as an argument to therun()function.
- SceneView.anti_alias#
Set this to True to enable 4x multisampling. This has a (sometimes significant) performance cost, and is disabled by default.
- SceneView.shows_fps#
Set to True to enable a debugging overlay that shows the current framerate.
Shader#
- class scene.Shader(shader_src)#
A
Shaderobject represents a custom OpenGL fragment shader that can be used to modify the rendering behavior ofSpriteNodeandEffectNodeobjects (via their respectiveshaderattributes).Shader programming is a complex topic, and a complete introduction is beyond the scope of this document, but the basic concept is really quite simple. A fragment shader is essentially a function/program that is executed directly on the GPU, and that is responsible for producing a color value for every pixel. Shaders are written in GLSL (GL Shading Language), which is very similar to C.
The default shader of a
SpriteNodebasically just uses its texture coordinate input (which is set automatically) to look up the corresponding color in its associatedTexture. A simple custom shader might either adjust the resulting color value (for example, converting the colors to grayscale), or modify the texture coordinates to produce morphological effects.Shaders have two kinds of inputs: Varyings and uniforms. Essentially, a uniform has the same value for every pixel/fragment, while a varying is interpolated. An example for a varying would be the texture coordinates, which are obviously different for every pixel. An example for a uniform would be a sprite’s size or the current timestamp.
A couple of varyings and uniforms are set automatically when a
SpriteNodeorEffectNodewith a custom shader is rendered:uniform float u_time– the current timestamp of the scene’s animation loopuniform vec2 u_sprite_size– The size of the sprite (in points)uniform float u_scale– The scale factor of the screen (typically 2.0 for retina screens) – this can be used to convertu_sprite_sizeto actual screen pixels.uniform sampler2D u_texture– The texture of the sprite (for anEffectNode, this texture contains the rendering of its children)uniform vec4 u_tint_color– The premultiplied color of the sprite (corresponding to theSpriteNode.colorattribute)uniform vec4 u_fill_color– If the sprite has no texture, this is used instead ofu_tint_color.varying vec2 v_tex_coord– The current texture (UV) coordinates
Even though these uniforms and varyings are set automatically, you have to declare them in your shader if you want to use them.
You can also declare and set custom uniforms using the Shader.set_uniform() method. Custom uniforms can be textures (sampler2D), floats, and 2-/3-/4-component vectors (vec2/vec3/vec4).
The following example produces an interesting ‘ripple’ effect on the Pythonista icon. It also demonstrates how you can modify shader behavior by setting uniforms – in this case, the center of the ripple effect is changed in response to touch events.:
from scene import *
ripple_shader = '''
precision highp float;
varying vec2 v_tex_coord;
// These uniforms are set automatically:
uniform sampler2D u_texture;
uniform float u_time;
uniform vec2 u_sprite_size;
// This uniform is set in response to touch events:
uniform vec2 u_offset;
void main(void) {
vec2 p = -1.0 + 2.0 * v_tex_coord + (u_offset / u_sprite_size * 2.0);
float len = length(p);
vec2 uv = v_tex_coord + (p/len) * 1.5 * cos(len*50.0 - u_time*10.0) * 0.03;
gl_FragColor = texture2D(u_texture,uv);
}
'''
class MyScene (Scene):
def setup(self):
self.sprite = SpriteNode('test:Pythonista', parent=self)
self.sprite.shader = Shader(ripple_shader)
self.did_change_size()
def did_change_size(self):
# Center the image:
self.sprite.position = self.size/2
def touch_began(self, touch):
self.set_ripple_center(touch)
def touch_moved(self, touch):
self.set_ripple_center(touch)
def set_ripple_center(self, touch):
# Center the ripple effect on the touch location by setting the `u_offset` shader uniform:
dx, dy = self.sprite.position - touch.location
self.sprite.shader.set_uniform('u_offset', (dx, dy))
run(MyScene())
- Shader.get_uniform(name)#
Return the current value of the uniform with the given name. Note that the uniform must be of type
float,vec2,vec3orvec4(i.e. sampler/texture uniforms are not supported by this method). For invalid uniform names, None is returned.
Action#
- class scene.Action#
An
Actionobject is an animation that usually changes aNode’s attributes over time. It can be added to a node using itsNode.run_action()method.Different types of actions (for different
Nodeattributes) are created using different class methods, e.g.Action.move_to(),Action.rotate_by()etc.Once an action has been added to a
Node, it cannot be changed anymore, but you can remove it (and stop the animation) usingNode.remove_action()orNode.remove_all_actions().Some actions modify the behavior of other actions:
An
Action.sequence()has multiple child actions. Each action in the sequence begins after the previous action ends.An
Action.group()has multiple child actions. All actions stored in the group begin executing at the same time. The entire group finishes after all actions have finished. This is particularly useful in combination withAction.sequence().An
Action.repeat()action stores a single child action. When the child action completes, it is restarted.Groups, sequences, and repeating actions can be nested. The ability to combine actions together allows you to add very sophisticated behaviors to a node.
The default duration of animated actions is 0.5 seconds.
- classmethod Action.call(func[, duration])#
Creates a custom action that calls a function (or other callable object).
If no duration parameter is passed, the function is called exactly once and must not take any parameters.
If the duration parameter is used, the node that the action is executed on, and the current progress (between 0.0 and 1.0) are passed to the function, and it must have the following signature:
def custom_action(node, progress): pass # do something with the node here...
- classmethod Action.fade_by(alpha[, duration, timing_mode])#
Creates an action that adjusts the alpha value of a node by a relative value.
When the action executes, the
Node.alphaattribute animates to its new value.
- classmethod Action.fade_to(alpha[, duration, timing_mode])#
Creates an action that adjusts the alpha value of a node to a new value.
When the action executes, the
Node.alphaattribute animates to its new value.
- classmethod Action.group(actions...)#
Creates an action that runs a collection of actions in parallel.
When the action executes, the actions that comprise the group all start immediately and run in parallel. The duration of the group action is the longest duration among the collection of actions. If an action in the group has a duration less than the group’s duration, the action completes, then idles until the group completes the remaining actions. This matters most when creating a repeating action that repeats a group.
You can pass the group’s actions as individual arguments or as a single sequence argument (e.g. a list).
- classmethod Action.move_by(dx, dy[, duration, timing_mode])#
Creates an action that moves a node relative to its current position.
- classmethod Action.move_to(x, y[, duration, timing_mode])#
Creates an action that moves a node to a new position.
- classmethod Action.repeat(action, repeat_count)#
Creates an action that repeats another action a specified number of times. If repeat_count is <=0, the action repeats forever (or until it is removed explicitly).
- classmethod Action.repeat_forever(action)#
Creates an action that repeats another action indefinitely.
- classmethod Action.rotate_by(radians[, duration, timing_mode])#
Creates an action that rotates the node by a relative value.
- classmethod Action.rotate_to(radians[, duration, timing_mode])#
Creates an action that rotates the node counterclockwise to an absolute angle.
- classmethod Action.scale_by(scale[, duration, timing_mode])#
Creates an action that changes the x and y scale values of a node by a relative value.
- classmethod Action.scale_to(scale[, duration, timing_mode])#
Creates an action that changes the x and y scale values of a node.
- classmethod Action.scale_x_to(scale[, duration, timing_mode])#
Creates an action that changes the x scale value of a node.
- classmethod Action.scale_y_to(scale[, duration, timing_mode])#
Creates an action that changes the y scale value of a node.
- classmethod Action.set_uniform(name, value[, duration, timing_mode])#
Creates an action that animates the given shader uniform to a new value. Note that this action only has an effect on
SpriteNodeandEffectNodeobjects. The value must be either a single number (forfloatuniforms) or a sequence of 2-4 numbers (forvec2,vec3andvec4uniforms).
- classmethod Action.sequence(actions...)#
Creates an action that runs a collection of actions sequentially.
When the action executes, the first action in the sequence starts and runs to completion. Subsequent actions in the sequence run in a similar fashion until all of the actions in the sequence have executed. The duration of the sequence action is the sum of the durations of the actions in the sequence.
You can pass the sequence’s actions as individual arguments or as a single sequence argument (e.g. a list).
- classmethod Action.wait(wait_duration)#
Creates an action that idles for a specified period of time.
When the action executes, the action waits for the specified amount of time, then ends. This is typically used as part of a sequence of actions to insert a delay between two other actions.
Action Attributes#
- Action.duration#
The duration of the action, in seconds. For some types of actions (e.g. groups, sequences), the duration is ignored. Note that changing the duration (or any other attributes) of an action after it has been added to a
Nodehas no effect.
- Action.timing_mode#
The timing mode used to execute an action.
The possible values for this property are listed under Timing Modes. The default value is
TIMING_LINEAR.For some types of actions (e.g. groups, sequences), the timing mode is ignored. Note that changing the timing mode (or any other attributes) of an action after it has been added to a
Nodehas no effect.
Texture#
- class scene.Texture(image)#
Textureobjects are used bySpriteNodeobjects to render their content. A texture is an image that has been loaded into the GPU’s memory. Textures can be initialized using either the name of a built-in image (a string), or aui.Imageobject.
- Texture.subtexture(rect)#
Create a new texture from a rectangular area in an existing texture. The returned texture object shares the same texture data as the original texture object, meaning that only one copy of the texture data is kept in memory.
The rect parameter describes the portion of the texture in unit coordinates, e.g.
(0, 0, 0.5, 0.5)would create a texture from the bottom-left quadrant of the original texture.This method can be used for sprite sheets / texture atlasses.
- Texture.filtering_mode#
The filtering mode that is used for scaling the texture. Can be one of the constants listed under Texture Filtering Modes (
FILTERING_LINEARby default).
- Texture.size#
The size of the texture in pixels. Note that this does not take the screen’s scale factor into account.
Touch#
- class scene.Touch(x, y, prev_x, prev_y, touch_id)#
Instances of this class are passed to the
Scene.touch_began(),Scene.touch_moved()andScene.touch_ended()methods.Sceneobjects also have a touches attribute (a dictionary that maps touch_id toTouchobjects).
Geometry Types#
Vector2#
- class scene.Vector2(x, y)#
The
Vector2class is the base class forPointandSize.Vectors (and subsequently points and sizes) support the addition (+), subtraction (-), multiplication (*) and division (/) operators. Addition and subtraction are supported for two vectors. Multiplication and division can also be applied to a vector and a scalar (number).
Passing a vector to the built-in
abs()function calculates the length of a vector. This can be particularly useful in combination with subtraction, making it very easy to calculate the distance between two points, e.g.abs(p2 - p1).It is also possible to determine whether a point lies within a
Rect, using the in operator.In most ways,
Vector2behaves like a sequence, similar to a 2-tuple. For example, you can alternatively access its x component using subscript notation (v[0]). Vectors also support iteration, argument unpacking, etc.Whenever a
Vector2,PointorSizeobject is used as an attribute in thescenemodule, you can alternatively provide any sequence of 4 numbers (e.g. a list or tuple).
- Vector2.x#
The x component of the vector.
- Vector2.y#
The y component of the vector.
Point#
Size#
Rect#
- class scene.Rect(x, y, w, h)#
The
Rectclass is used for bounding boxes and other rectangle values, e.g. theNode.frameattribute. A rectangle is represented as (x, y, w[idth], h[eight]), with (x, y) being its lower-left corner.In most ways,
Rectbehaves like a sequence, similar to a 4-tuple. For example, you can alternatively access its x component using subscript notation (r[0]). Rectangles also support iteration, argument unpacking, etc.Whenever a
Rectobject is used as an attribute in thescenemodule, you can alternatively provide any sequence of 4 numbers (e.g. a list or tuple).
- Rect.x#
The x component of the rectangle’s lower-left corner.
- Rect.y#
The y component of the rectangle’s lower-left corner.
- Rect.w#
- Rect.width#
The width of the rectangle.
- Rect.h#
- Rect.height#
The height of the rectangle.
- Rect.origin#
Equivalent to
Point(rect.x, rect.y)
- Rect.size#
Equivalent to
Size(rect.w, rect.h)
- Rect.min_x#
Equivalent to
min(rect.x, rect.x + rect.w)(the x component of the left edge)
- Rect.max_x#
Equivalent to
max(rect.x, rect.x + rect.w)(the x component of the right edge)
- Rect.min_y#
Equivalent to
min(rect.y, rect.y + rect.h)(the y component of the bottom edge)
- Rect.max_y#
Equivalent to
max(rect.y, rect.y + rect.h)(the y component of the top edge)
- Rect.center([p])#
When called without arguments, return the center of the rectangle. When a
Pointis passed as an argument, the rectangle’s x and y values are adjusted, so that the new center of the rectangle is p.
- Rect.contains_point(p)#
Return True if the given point lies within the bounds of the rectangle, False otherwise.
- Rect.contains_rect(other_rect)#
Return True if the given rectangle lies entirely within the bounds of this rectangle, False otherwise.
- Rect.intersects(other_rect)#
Return True if this rectangle intersects with the other rectangle, False otherwise.
- Rect.intersection(other_rect)#
Return a
Rectthat corresponds to the intersection of this rectangle with the other one.
- Rect.translate(x, y)#
Equivalent to
Rect(r.x + x, r.y + y, r.w, r.h)
- Rect.inset(top, left[, bottom, right])#
Return a rectangle that is adjusted by the given edge insets. bottom/right are optional and default to the same value as top/left.
Functions#
- scene.gravity()#
Return the current gravity vector (x, y, z) – each component value will be between 0.0 and 1.0. This can be used to determine the current orientation of the device, typically for motion-controlled games.
- scene.get_screen_size()#
Return the screen size (in points) as a
Sizeobject. Note that the value is orientation-dependent – you may want to usemin(get_screen_size())to get the smallest dimension in any orientation.
- scene.get_screen_scale()#
Return the scale factor of the current device’s screen. For retina screens this will usually be 2.0 or 3.0, and 1.0 for non-retina screens.
- scene.get_image_path(name)#
Return the absolute path of the file that corresponds to a built-in image name.
- scene.run(scene, orientation=DEFAULT_ORIENTATION, frame_interval=1, anti_alias=False, show_fps=False, multi_touch=True)#
Runs the given
Sceneobject. By default, the scene runs in the current device orientation, set the parameter to PORTRAIT or LANDSCAPE to force a specific orientation (and to disallow auto-rotation). Important: The orientation parameter has no effect on iPads starting with iOS 10 because it is technically not possible to lock the orientation in an app that supports split-screen multitasking.By default, the scene’s
update()method is called 60 times per second. Set the frame_interval parameter to 2 for 30fps, 3 for 20, etc.
- scene.get_controllers()#
Return the current state of all connected MFi game controllers as a list of dicts (see also Game Controllers).
Constants#
Orientations#
The following constants are used for the orientation parameter of the run() function.
- scene.DEFAULT_ORIENTATION#
Start the scene in the current device orientation, allow auto-rotation.
- scene.PORTRAIT#
Force portrait orientation.
- scene.LANDSCAPE#
Force landscape orientation.
Blend Modes#
- scene.BLEND_NORMAL#
The source and destination colors are blended by multiplying the source alpha value.
- scene.BLEND_ADD#
The source and destination colors are added together.
- scene.BLEND_MULTIPLY#
The source color is multiplied by the destination color.
Texture Filtering Modes#
The following constants can be used for the Texture.filtering_mode attribute.
- scene.FILTERING_LINEAR#
Linear interpolation, the default filtering mode
- scene.FILTERING_NEAREST#
Nearest-neighbor interpolation – this is the recommended mode for pixel art because it results in sharp edges when scaling up low-resolution artwork.
Timing Modes#
The following constants can be used for the timing_mode attribute of Action objects to determine their interpolation function.
- scene.TIMING_LINEAR#
Simple linear interpolation (the default).
- scene.TIMING_EASE_IN#
Ease-in interpolation
- scene.TIMING_EASE_IN_2#
Alternative ease-in interpolation (quadratic)
- scene.TIMING_EASE_OUT#
Ease-out interpolation
- scene.TIMING_EASE_OUT_2#
Alternative ease-out interpolation (quadratic)
- scene.TIMING_EASE_IN_OUT#
Ease-in-ease-out interpolation.
- scene.TIMING_SINODIAL#
Similar to
TIMING_EASE_IN_OUT, but with slightly less pronounced easing phases.
- scene.TIMING_ELASTIC_IN#
“Rubberband” effect at the start of the animation.
- scene.TIMING_ELASTIC_OUT#
“Rubberband” effect at the end of the animation.
- scene.TIMING_ELASTIC_IN_OUT#
“Rubberband” effect at both ends of the animation.
- scene.TIMING_BOUNCE_IN#
“Bouncing” effect at the start of the animation.
- scene.TIMING_BOUNCE_OUT#
“Bouncing” effect at the end of the animation.
- scene.TIMING_BOUNCE_IN_OUT#
“Bouncing” effect at both ends of the animation.
- scene.TIMING_EASE_BACK_IN#
Overshooting animation, eased at the start.
- scene.TIMING_EASE_BACK_OUT#
Overshooting animation, eased at the end.
- scene.TIMING_EASE_BACK_IN_OUT#
Overshooting animation, eased at the start and end.