Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(657)

Side by Side Diff: sky/examples/game/lib/sprite_box.dart

Issue 1218593002: Move sky/examples to sky/sdk/lib/example, and code changes to support that change. Fixes T277. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 part of sprites;
2
3 /// Options for setting up a [SpriteBox].
4 ///
5 /// * [nativePoints], use the same points as the parent [Widget].
6 /// * [letterbox], use the size of the root node for the coordinate system, con strain the aspect ratio and trim off
7 /// areas that end up outside the screen.
8 /// * [stretch], use the size of the root node for the coordinate system, scale it to fit the size of the box.
9 /// * [scaleToFit], similar to the letterbox option, but instead of trimming ar eas the sprite system will be scaled
10 /// down to fit the box.
11 /// * [fixedWidth], uses the width of the root node to set the size of the coor dinate system, this option will change
12 /// the height of the root node to fit the box.
13 /// * [fixedHeight], uses the height of the root node to set the size of the co ordinate system, this option will change
14 /// the width of the root node to fit the box.
15 enum SpriteBoxTransformMode {
16 nativePoints,
17 letterbox,
18 stretch,
19 scaleToFit,
20 fixedWidth,
21 fixedHeight,
22 }
23
24 class SpriteBox extends RenderBox {
25
26 // Member variables
27
28 // Root node for drawing
29 NodeWithSize _rootNode;
30
31 // Tracking of frame rate and updates
32 double _lastTimeStamp;
33 int _numFrames = 0;
34 double _frameRate = 0.0;
35
36 // Transformation mode
37 SpriteBoxTransformMode _transformMode;
38
39 /// The transform mode used by the [SpriteBox].
40 SpriteBoxTransformMode get transformMode => _transformMode;
41
42 // Cached transformation matrix
43 Matrix4 _transformMatrix;
44
45 List<Node> _eventTargets;
46
47 // Setup
48
49 /// Creates a new SpriteBox with a node as its content, by default uses letter boxing.
50 ///
51 /// The [rootNode] provides the content of the node tree, typically it's a cus tom subclass of [NodeWithSize]. The
52 /// [mode] provides different ways to scale the content to best fit it to the screen. In most cases it's preferred to
53 /// use a [SpriteWidget] that automatically wraps the SpriteBox.
54 ///
55 /// var spriteBox = new SpriteBox(myNode, SpriteBoxTransformMode.fixedHeig ht);
56 SpriteBox(NodeWithSize rootNode, [SpriteBoxTransformMode mode = SpriteBoxTrans formMode.letterbox]) {
57 assert(rootNode != null);
58 assert(rootNode._spriteBox == null);
59
60 // Setup root node
61 _rootNode = rootNode;
62
63 // Assign SpriteBox reference to all the nodes
64 _addSpriteBoxReference(_rootNode);
65
66 // Setup transform mode
67 _transformMode = mode;
68
69 _scheduleTick();
70 }
71
72 void _addSpriteBoxReference(Node node) {
73 node._spriteBox = this;
74 for (Node child in node._children) {
75 _addSpriteBoxReference(child);
76 }
77 }
78
79 // Properties
80
81 /// The root node of the node tree that is rendered by this box.
82 ///
83 /// var rootNode = mySpriteBox.rootNode;
84 NodeWithSize get rootNode => _rootNode;
85
86 void performLayout() {
87 size = constraints.constrain(Size.infinite);
88 _invalidateTransformMatrix();
89 _callSpriteBoxPerformedLayout(_rootNode);
90 }
91
92 // Event handling
93
94 void _addEventTargets(Node node, List<Node> eventTargets) {
95 List children = node.children;
96 int i = 0;
97
98 // Add childrens that are behind this node
99 while (i < children.length) {
100 Node child = children[i];
101 if (child.zPosition >= 0.0) break;
102 _addEventTargets(child, eventTargets);
103 i++;
104 }
105
106 // Add this node
107 if (node.userInteractionEnabled) {
108 eventTargets.add(node);
109 }
110
111 // Add children in front of this node
112 while (i < children.length) {
113 Node child = children[i];
114 _addEventTargets(child, eventTargets);
115 i++;
116 }
117 }
118
119 void handleEvent(Event event, _SpriteBoxHitTestEntry entry) {
120 if (event is PointerEvent) {
121
122 if (event.type == 'pointerdown') {
123 // Build list of event targets
124 if (_eventTargets == null) {
125 _eventTargets = [];
126 _addEventTargets(_rootNode, _eventTargets);
127 }
128
129 // Find the once that are hit by the pointer
130 List<Node> nodeTargets = [];
131 for (int i = _eventTargets.length - 1; i >= 0; i--) {
132 Node node = _eventTargets[i];
133
134 // Check if the node is ready to handle a pointer
135 if (node.handleMultiplePointers || node._handlingPointer == null) {
136 // Do the hit test
137 Point posInNodeSpace = node.convertPointToNodeSpace(entry.localPosit ion);
138 if (node.isPointInside(posInNodeSpace)) {
139 nodeTargets.add(node);
140 node._handlingPointer = event.pointer;
141 }
142 }
143 }
144
145 entry.nodeTargets = nodeTargets;
146 }
147
148 // Pass the event down to nodes that were hit by the pointerdown
149 List<Node> targets = entry.nodeTargets;
150 for (Node node in targets) {
151 // Check if this event should be dispatched
152 if (node.handleMultiplePointers || event.pointer == node._handlingPointe r) {
153 // Dispatch event
154 bool consumedEvent = node.handleEvent(new SpriteBoxEvent(new Point(eve nt.x, event.y), event.type, event.pointer));
155 if (consumedEvent == null || consumedEvent) break;
156 }
157 }
158
159 // De-register pointer for nodes that doesn't handle multiple pointers
160 for (Node node in targets) {
161 if (event.type == 'pointerup' || event.type == 'pointercancel') {
162 node._handlingPointer = null;
163 }
164 }
165 }
166 }
167
168 bool hitTest(HitTestResult result, { Point position }) {
169 result.add(new _SpriteBoxHitTestEntry(this, position));
170 return true;
171 }
172
173 // Rendering
174
175 /// The transformation matrix used to transform the root node to the space of the box.
176 ///
177 /// It's uncommon to need access to this property.
178 ///
179 /// var matrix = mySpriteBox.transformMatrix;
180 Matrix4 get transformMatrix {
181 // Get cached matrix if available
182 if (_transformMatrix != null) {
183 return _transformMatrix;
184 }
185
186 _transformMatrix = new Matrix4.identity();
187
188 // Calculate matrix
189 double scaleX = 1.0;
190 double scaleY = 1.0;
191 double offsetX = 0.0;
192 double offsetY = 0.0;
193
194 double systemWidth = rootNode.size.width;
195 double systemHeight = rootNode.size.height;
196
197 switch(_transformMode) {
198 case SpriteBoxTransformMode.stretch:
199 scaleX = size.width/systemWidth;
200 scaleY = size.height/systemHeight;
201 break;
202 case SpriteBoxTransformMode.letterbox:
203 scaleX = size.width/systemWidth;
204 scaleY = size.height/systemHeight;
205 if (scaleX > scaleY) {
206 scaleY = scaleX;
207 offsetY = (size.height - scaleY * systemHeight)/2.0;
208 } else {
209 scaleX = scaleY;
210 offsetX = (size.width - scaleX * systemWidth)/2.0;
211 }
212 break;
213 case SpriteBoxTransformMode.scaleToFit:
214 scaleX = size.width/systemWidth;
215 scaleY = size.height/systemHeight;
216 if (scaleX < scaleY) {
217 scaleY = scaleX;
218 offsetY = (size.height - scaleY * systemHeight)/2.0;
219 } else {
220 scaleX = scaleY;
221 offsetX = (size.width - scaleX * systemWidth)/2.0;
222 }
223 break;
224 case SpriteBoxTransformMode.fixedWidth:
225 scaleX = size.width/systemWidth;
226 scaleY = scaleX;
227 systemHeight = size.height/scaleX;
228 rootNode.size = new Size(systemWidth, systemHeight);
229 break;
230 case SpriteBoxTransformMode.fixedHeight:
231 scaleY = size.height/systemHeight;
232 scaleX = scaleY;
233 systemWidth = size.width/scaleY;
234 rootNode.size = new Size(systemWidth, systemHeight);
235 break;
236 case SpriteBoxTransformMode.nativePoints:
237 break;
238 default:
239 assert(false);
240 break;
241 }
242
243 _transformMatrix.translate(offsetX, offsetY);
244 _transformMatrix.scale(scaleX, scaleY);
245
246 return _transformMatrix;
247 }
248
249 void _invalidateTransformMatrix() {
250 _transformMatrix = null;
251 _rootNode._invalidateToBoxTransformMatrix();
252 }
253
254 void paint(RenderCanvas canvas) {
255 canvas.save();
256
257 // Move to correct coordinate space before drawing
258 canvas.concat(transformMatrix.storage);
259
260 // Draw the sprite tree
261 _rootNode._visit(canvas);
262
263 canvas.restore();
264 }
265
266 // Updates
267
268 int _animationId = 0;
269
270 void _scheduleTick() {
271 _animationId = scheduler.requestAnimationFrame(_tick);
272 }
273
274 void _tick(double timeStamp) {
275
276 // Calculate the time between frames in seconds
277 if (_lastTimeStamp == null) _lastTimeStamp = timeStamp;
278 double delta = (timeStamp - _lastTimeStamp) / 1000;
279 _lastTimeStamp = timeStamp;
280
281 // Count the number of frames we've been running
282 _numFrames += 1;
283
284 _frameRate = 1.0/delta;
285
286 // Print frame rate
287 if (_numFrames % 60 == 0) print("delta: $delta fps: $_frameRate");
288
289 _callUpdate(_rootNode, delta);
290 _scheduleTick();
291 }
292
293 void _callUpdate(Node node, double dt) {
294 node.update(dt);
295 for (Node child in node.children) {
296 if (!child.paused) {
297 _callUpdate(child, dt);
298 }
299 }
300 }
301
302 void _callSpriteBoxPerformedLayout(Node node) {
303 node.spriteBoxPerformedLayout();
304 for (Node child in node.children) {
305 _callSpriteBoxPerformedLayout(child);
306 }
307 }
308
309 // Hit tests
310
311 /// Finds all nodes at a position defined in the box's coordinates.
312 ///
313 /// Use this method with caution. It searches the complete node tree to locate the nodes, which can be slow if the
314 /// node tree is large.
315 ///
316 /// List nodes = mySpriteBox.findNodesAtPosition(new Point(50.0, 50.0));
317 List<Node> findNodesAtPosition(Point position) {
318 assert(position != null);
319
320 List<Node> nodes = [];
321
322 // Traverse the render tree and find objects at the position
323 _addNodesAtPosition(_rootNode, position, nodes);
324
325 return nodes;
326 }
327
328 _addNodesAtPosition(Node node, Point position, List<Node> list) {
329 // Visit children first
330 for (Node child in node.children) {
331 _addNodesAtPosition(child, position, list);
332 }
333 // Do the hit test
334 Point posInNodeSpace = node.convertPointToNodeSpace(position);
335 if (node.isPointInside(posInNodeSpace)) {
336 list.add(node);
337 }
338 }
339 }
340
341 class _SpriteBoxHitTestEntry extends BoxHitTestEntry {
342 List<Node> nodeTargets;
343 _SpriteBoxHitTestEntry(RenderBox target, Point localPosition) : super(target, localPosition);
344 }
345
346 /// An event that is passed down the node tree when pointer events occur. The Sp riteBoxEvent is typically handled in
347 /// the handleEvent method of [Node].
348 class SpriteBoxEvent {
349
350 /// The position of the event in box coordinates.
351 ///
352 /// You can use the convertPointToNodeSpace of [Node] to convert the position to local coordinates.
353 ///
354 /// bool handleEvent(SpriteBoxEvent event) {
355 /// Point localPosition = convertPointToNodeSpace(event.boxPosition);
356 /// if (event.type == 'pointerdown') {
357 /// // Do something!
358 /// }
359 /// }
360 final Point boxPosition;
361
362 /// The type of event, there are currently four valid types, 'pointerdown', 'p ointermoved', 'pointerup', and
363 /// 'pointercancel'.
364 ///
365 /// if (event.type == 'pointerdown') {
366 /// // Do something!
367 /// }
368 final String type;
369
370 /// The id of the pointer. Each pointer on the screen will have a unique point er id.
371 ///
372 /// if (event.pointer == firstPointerId) {
373 /// // Do something
374 /// }
375 final int pointer;
376
377 /// Creates a new SpriteBoxEvent, typically this is done internally inside the SpriteBox.
378 ///
379 /// var event = new SpriteBoxEvent(new Point(50.0, 50.0), 'pointerdown', 0 );
380 SpriteBoxEvent(this.boxPosition, this.type, this.pointer);
381 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698