Index: sky/examples/game/lib/sprite_box.dart |
diff --git a/sky/examples/game/lib/sprite_box.dart b/sky/examples/game/lib/sprite_box.dart |
deleted file mode 100644 |
index f3020316b9017cf292328d31ddffb5c288acba67..0000000000000000000000000000000000000000 |
--- a/sky/examples/game/lib/sprite_box.dart |
+++ /dev/null |
@@ -1,381 +0,0 @@ |
-part of sprites; |
- |
-/// Options for setting up a [SpriteBox]. |
-/// |
-/// * [nativePoints], use the same points as the parent [Widget]. |
-/// * [letterbox], use the size of the root node for the coordinate system, constrain the aspect ratio and trim off |
-/// areas that end up outside the screen. |
-/// * [stretch], use the size of the root node for the coordinate system, scale it to fit the size of the box. |
-/// * [scaleToFit], similar to the letterbox option, but instead of trimming areas the sprite system will be scaled |
-/// down to fit the box. |
-/// * [fixedWidth], uses the width of the root node to set the size of the coordinate system, this option will change |
-/// the height of the root node to fit the box. |
-/// * [fixedHeight], uses the height of the root node to set the size of the coordinate system, this option will change |
-/// the width of the root node to fit the box. |
-enum SpriteBoxTransformMode { |
- nativePoints, |
- letterbox, |
- stretch, |
- scaleToFit, |
- fixedWidth, |
- fixedHeight, |
-} |
- |
-class SpriteBox extends RenderBox { |
- |
- // Member variables |
- |
- // Root node for drawing |
- NodeWithSize _rootNode; |
- |
- // Tracking of frame rate and updates |
- double _lastTimeStamp; |
- int _numFrames = 0; |
- double _frameRate = 0.0; |
- |
- // Transformation mode |
- SpriteBoxTransformMode _transformMode; |
- |
- /// The transform mode used by the [SpriteBox]. |
- SpriteBoxTransformMode get transformMode => _transformMode; |
- |
- // Cached transformation matrix |
- Matrix4 _transformMatrix; |
- |
- List<Node> _eventTargets; |
- |
- // Setup |
- |
- /// Creates a new SpriteBox with a node as its content, by default uses letterboxing. |
- /// |
- /// The [rootNode] provides the content of the node tree, typically it's a custom subclass of [NodeWithSize]. The |
- /// [mode] provides different ways to scale the content to best fit it to the screen. In most cases it's preferred to |
- /// use a [SpriteWidget] that automatically wraps the SpriteBox. |
- /// |
- /// var spriteBox = new SpriteBox(myNode, SpriteBoxTransformMode.fixedHeight); |
- SpriteBox(NodeWithSize rootNode, [SpriteBoxTransformMode mode = SpriteBoxTransformMode.letterbox]) { |
- assert(rootNode != null); |
- assert(rootNode._spriteBox == null); |
- |
- // Setup root node |
- _rootNode = rootNode; |
- |
- // Assign SpriteBox reference to all the nodes |
- _addSpriteBoxReference(_rootNode); |
- |
- // Setup transform mode |
- _transformMode = mode; |
- |
- _scheduleTick(); |
- } |
- |
- void _addSpriteBoxReference(Node node) { |
- node._spriteBox = this; |
- for (Node child in node._children) { |
- _addSpriteBoxReference(child); |
- } |
- } |
- |
- // Properties |
- |
- /// The root node of the node tree that is rendered by this box. |
- /// |
- /// var rootNode = mySpriteBox.rootNode; |
- NodeWithSize get rootNode => _rootNode; |
- |
- void performLayout() { |
- size = constraints.constrain(Size.infinite); |
- _invalidateTransformMatrix(); |
- _callSpriteBoxPerformedLayout(_rootNode); |
- } |
- |
- // Event handling |
- |
- void _addEventTargets(Node node, List<Node> eventTargets) { |
- List children = node.children; |
- int i = 0; |
- |
- // Add childrens that are behind this node |
- while (i < children.length) { |
- Node child = children[i]; |
- if (child.zPosition >= 0.0) break; |
- _addEventTargets(child, eventTargets); |
- i++; |
- } |
- |
- // Add this node |
- if (node.userInteractionEnabled) { |
- eventTargets.add(node); |
- } |
- |
- // Add children in front of this node |
- while (i < children.length) { |
- Node child = children[i]; |
- _addEventTargets(child, eventTargets); |
- i++; |
- } |
- } |
- |
- void handleEvent(Event event, _SpriteBoxHitTestEntry entry) { |
- if (event is PointerEvent) { |
- |
- if (event.type == 'pointerdown') { |
- // Build list of event targets |
- if (_eventTargets == null) { |
- _eventTargets = []; |
- _addEventTargets(_rootNode, _eventTargets); |
- } |
- |
- // Find the once that are hit by the pointer |
- List<Node> nodeTargets = []; |
- for (int i = _eventTargets.length - 1; i >= 0; i--) { |
- Node node = _eventTargets[i]; |
- |
- // Check if the node is ready to handle a pointer |
- if (node.handleMultiplePointers || node._handlingPointer == null) { |
- // Do the hit test |
- Point posInNodeSpace = node.convertPointToNodeSpace(entry.localPosition); |
- if (node.isPointInside(posInNodeSpace)) { |
- nodeTargets.add(node); |
- node._handlingPointer = event.pointer; |
- } |
- } |
- } |
- |
- entry.nodeTargets = nodeTargets; |
- } |
- |
- // Pass the event down to nodes that were hit by the pointerdown |
- List<Node> targets = entry.nodeTargets; |
- for (Node node in targets) { |
- // Check if this event should be dispatched |
- if (node.handleMultiplePointers || event.pointer == node._handlingPointer) { |
- // Dispatch event |
- bool consumedEvent = node.handleEvent(new SpriteBoxEvent(new Point(event.x, event.y), event.type, event.pointer)); |
- if (consumedEvent == null || consumedEvent) break; |
- } |
- } |
- |
- // De-register pointer for nodes that doesn't handle multiple pointers |
- for (Node node in targets) { |
- if (event.type == 'pointerup' || event.type == 'pointercancel') { |
- node._handlingPointer = null; |
- } |
- } |
- } |
- } |
- |
- bool hitTest(HitTestResult result, { Point position }) { |
- result.add(new _SpriteBoxHitTestEntry(this, position)); |
- return true; |
- } |
- |
- // Rendering |
- |
- /// The transformation matrix used to transform the root node to the space of the box. |
- /// |
- /// It's uncommon to need access to this property. |
- /// |
- /// var matrix = mySpriteBox.transformMatrix; |
- Matrix4 get transformMatrix { |
- // Get cached matrix if available |
- if (_transformMatrix != null) { |
- return _transformMatrix; |
- } |
- |
- _transformMatrix = new Matrix4.identity(); |
- |
- // Calculate matrix |
- double scaleX = 1.0; |
- double scaleY = 1.0; |
- double offsetX = 0.0; |
- double offsetY = 0.0; |
- |
- double systemWidth = rootNode.size.width; |
- double systemHeight = rootNode.size.height; |
- |
- switch(_transformMode) { |
- case SpriteBoxTransformMode.stretch: |
- scaleX = size.width/systemWidth; |
- scaleY = size.height/systemHeight; |
- break; |
- case SpriteBoxTransformMode.letterbox: |
- scaleX = size.width/systemWidth; |
- scaleY = size.height/systemHeight; |
- if (scaleX > scaleY) { |
- scaleY = scaleX; |
- offsetY = (size.height - scaleY * systemHeight)/2.0; |
- } else { |
- scaleX = scaleY; |
- offsetX = (size.width - scaleX * systemWidth)/2.0; |
- } |
- break; |
- case SpriteBoxTransformMode.scaleToFit: |
- scaleX = size.width/systemWidth; |
- scaleY = size.height/systemHeight; |
- if (scaleX < scaleY) { |
- scaleY = scaleX; |
- offsetY = (size.height - scaleY * systemHeight)/2.0; |
- } else { |
- scaleX = scaleY; |
- offsetX = (size.width - scaleX * systemWidth)/2.0; |
- } |
- break; |
- case SpriteBoxTransformMode.fixedWidth: |
- scaleX = size.width/systemWidth; |
- scaleY = scaleX; |
- systemHeight = size.height/scaleX; |
- rootNode.size = new Size(systemWidth, systemHeight); |
- break; |
- case SpriteBoxTransformMode.fixedHeight: |
- scaleY = size.height/systemHeight; |
- scaleX = scaleY; |
- systemWidth = size.width/scaleY; |
- rootNode.size = new Size(systemWidth, systemHeight); |
- break; |
- case SpriteBoxTransformMode.nativePoints: |
- break; |
- default: |
- assert(false); |
- break; |
- } |
- |
- _transformMatrix.translate(offsetX, offsetY); |
- _transformMatrix.scale(scaleX, scaleY); |
- |
- return _transformMatrix; |
- } |
- |
- void _invalidateTransformMatrix() { |
- _transformMatrix = null; |
- _rootNode._invalidateToBoxTransformMatrix(); |
- } |
- |
- void paint(RenderCanvas canvas) { |
- canvas.save(); |
- |
- // Move to correct coordinate space before drawing |
- canvas.concat(transformMatrix.storage); |
- |
- // Draw the sprite tree |
- _rootNode._visit(canvas); |
- |
- canvas.restore(); |
- } |
- |
- // Updates |
- |
- int _animationId = 0; |
- |
- void _scheduleTick() { |
- _animationId = scheduler.requestAnimationFrame(_tick); |
- } |
- |
- void _tick(double timeStamp) { |
- |
- // Calculate the time between frames in seconds |
- if (_lastTimeStamp == null) _lastTimeStamp = timeStamp; |
- double delta = (timeStamp - _lastTimeStamp) / 1000; |
- _lastTimeStamp = timeStamp; |
- |
- // Count the number of frames we've been running |
- _numFrames += 1; |
- |
- _frameRate = 1.0/delta; |
- |
- // Print frame rate |
- if (_numFrames % 60 == 0) print("delta: $delta fps: $_frameRate"); |
- |
- _callUpdate(_rootNode, delta); |
- _scheduleTick(); |
- } |
- |
- void _callUpdate(Node node, double dt) { |
- node.update(dt); |
- for (Node child in node.children) { |
- if (!child.paused) { |
- _callUpdate(child, dt); |
- } |
- } |
- } |
- |
- void _callSpriteBoxPerformedLayout(Node node) { |
- node.spriteBoxPerformedLayout(); |
- for (Node child in node.children) { |
- _callSpriteBoxPerformedLayout(child); |
- } |
- } |
- |
- // Hit tests |
- |
- /// Finds all nodes at a position defined in the box's coordinates. |
- /// |
- /// Use this method with caution. It searches the complete node tree to locate the nodes, which can be slow if the |
- /// node tree is large. |
- /// |
- /// List nodes = mySpriteBox.findNodesAtPosition(new Point(50.0, 50.0)); |
- List<Node> findNodesAtPosition(Point position) { |
- assert(position != null); |
- |
- List<Node> nodes = []; |
- |
- // Traverse the render tree and find objects at the position |
- _addNodesAtPosition(_rootNode, position, nodes); |
- |
- return nodes; |
- } |
- |
- _addNodesAtPosition(Node node, Point position, List<Node> list) { |
- // Visit children first |
- for (Node child in node.children) { |
- _addNodesAtPosition(child, position, list); |
- } |
- // Do the hit test |
- Point posInNodeSpace = node.convertPointToNodeSpace(position); |
- if (node.isPointInside(posInNodeSpace)) { |
- list.add(node); |
- } |
- } |
-} |
- |
-class _SpriteBoxHitTestEntry extends BoxHitTestEntry { |
- List<Node> nodeTargets; |
- _SpriteBoxHitTestEntry(RenderBox target, Point localPosition) : super(target, localPosition); |
-} |
- |
-/// An event that is passed down the node tree when pointer events occur. The SpriteBoxEvent is typically handled in |
-/// the handleEvent method of [Node]. |
-class SpriteBoxEvent { |
- |
- /// The position of the event in box coordinates. |
- /// |
- /// You can use the convertPointToNodeSpace of [Node] to convert the position to local coordinates. |
- /// |
- /// bool handleEvent(SpriteBoxEvent event) { |
- /// Point localPosition = convertPointToNodeSpace(event.boxPosition); |
- /// if (event.type == 'pointerdown') { |
- /// // Do something! |
- /// } |
- /// } |
- final Point boxPosition; |
- |
- /// The type of event, there are currently four valid types, 'pointerdown', 'pointermoved', 'pointerup', and |
- /// 'pointercancel'. |
- /// |
- /// if (event.type == 'pointerdown') { |
- /// // Do something! |
- /// } |
- final String type; |
- |
- /// The id of the pointer. Each pointer on the screen will have a unique pointer id. |
- /// |
- /// if (event.pointer == firstPointerId) { |
- /// // Do something |
- /// } |
- final int pointer; |
- |
- /// Creates a new SpriteBoxEvent, typically this is done internally inside the SpriteBox. |
- /// |
- /// var event = new SpriteBoxEvent(new Point(50.0, 50.0), 'pointerdown', 0); |
- SpriteBoxEvent(this.boxPosition, this.type, this.pointer); |
-} |