Merge pull request #202 from vlidholt/master

Fixes issues with invalidating matrix for SpriteBox & adds HUD to demo game
diff --git a/packages/flutter/example/game/lib/game_demo_world.dart b/packages/flutter/example/game/lib/game_demo_world.dart
index db93d6f..9154079 100644
--- a/packages/flutter/example/game/lib/game_demo_world.dart
+++ b/packages/flutter/example/game/lib/game_demo_world.dart
@@ -27,6 +27,7 @@
   sky.Image _imgNebula;
 
   SpriteSheet _spriteSheet;
+  SpriteSheet _spriteSheetUI;
   Navigator _navigator;
 
   // Inputs
@@ -46,8 +47,10 @@
   int _numFrames = 0;
   bool _isGameOver = false;
 
-  GameDemoWorld(App app, this._navigator, ImageMap images, this._spriteSheet) : super(new Size(_gameSizeWidth, _gameSizeHeight)) {
+  // Heads up display
+  Hud _hud;
 
+  GameDemoWorld(App app, this._navigator, ImageMap images, this._spriteSheet, this._spriteSheetUI) : super(new Size(_gameSizeWidth, _gameSizeHeight)) {
     // Fetch images
     _imgNebula = images["assets/nebula.png"];
 
@@ -81,6 +84,10 @@
 
     userInteractionEnabled = true;
     handleMultiplePointers = true;
+
+    _hud = new Hud(_spriteSheetUI);
+    _hud.zPosition = 1000.0;
+    addChild(_hud);
   }
 
   // Methods for adding game objects
@@ -585,6 +592,52 @@
   }
 }
 
+class Hud extends NodeWithSize {
+  SpriteSheet spriteSheetUI;
+  Sprite sprtBgScore;
+  Sprite sprtBgShield;
+
+  int _score = 0;
+
+  int get score => _score;
+
+  set score(int score) {
+    _score = score;
+    _updateHud();
+  }
+
+  Hud(this.spriteSheetUI) {
+    pivot = Point.origin;
+
+    sprtBgScore = new Sprite(spriteSheetUI["scoreboard.png"]);
+    sprtBgScore.pivot = Point.origin;
+    sprtBgScore.scale = 0.6;
+    addChild(sprtBgScore);
+
+    sprtBgShield = new Sprite(spriteSheetUI["bar_shield.png"]);
+    sprtBgShield.pivot = new Point(1.0, 0.0);
+    sprtBgShield.scale = 0.6;
+    addChild(sprtBgShield);
+  }
+
+  void spriteBoxPerformedLayout() {
+    // Set the size and position of HUD display
+    position = spriteBox.visibleArea.topLeft;
+    size = spriteBox.visibleArea.size;
+
+    // Position hud objects
+    sprtBgScore.position = new Point(20.0, 20.0);
+    sprtBgShield.position = new Point(size.width - 20.0, 20.0);
+  }
+
+  void _updateHud() {
+    sprtBgScore.removeAllChildren();
+
+    String scoreStr = _score.toString();
+    
+  }
+}
+
 class Nebula extends Node {
 
   Nebula.withImage(sky.Image img) {
diff --git a/packages/flutter/example/game/lib/sprite_box.dart b/packages/flutter/example/game/lib/sprite_box.dart
index c35d9e9..dd00bcf 100644
--- a/packages/flutter/example/game/lib/sprite_box.dart
+++ b/packages/flutter/example/game/lib/sprite_box.dart
@@ -39,6 +39,7 @@
 
     // Add new references
     _addSpriteBoxReference(_rootNode);
+    markNeedsLayout();
   }
 
   // Tracking of frame rate and updates
@@ -55,7 +56,7 @@
     _transformMode = value;
 
     // Invalidate stuff
-    if (attached) performLayout();
+    markNeedsLayout();
   }
 
   /// The transform mode used by the [SpriteBox].
@@ -68,7 +69,11 @@
 
   Rect _visibleArea;
 
-  Rect get visibleArea => _visibleArea;
+  Rect get visibleArea {
+    if (_visibleArea == null)
+      _calcTransformMatrix();
+    return _visibleArea;
+  }
 
   // Setup
 
@@ -131,7 +136,8 @@
     // Add childrens that are behind this node
     while (i < children.length) {
       Node child = children[i];
-      if (child.zPosition >= 0.0) break;
+      if (child.zPosition >= 0.0)
+        break;
       _addEventTargets(child, eventTargets);
       i++;
     }
@@ -150,6 +156,9 @@
   }
 
   void handleEvent(Event event, _SpriteBoxHitTestEntry entry) {
+    if (!attached)
+      return;
+
     if (event is PointerEvent) {
 
       if (event.type == 'pointerdown') {
@@ -185,7 +194,8 @@
         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;
+          if (consumedEvent == null || consumedEvent)
+            break;
         }
       }
 
@@ -212,10 +222,13 @@
   ///     var matrix = mySpriteBox.transformMatrix;
   Matrix4 get transformMatrix {
     // Get cached matrix if available
-    if (_transformMatrix != null) {
-      return _transformMatrix;
+    if (_transformMatrix == null) {
+      _calcTransformMatrix();
     }
+    return _transformMatrix;
+  }
 
+  void _calcTransformMatrix() {
     _transformMatrix = new Matrix4.identity();
 
     // Calculate matrix
@@ -273,13 +286,17 @@
         break;
     }
 
+    _visibleArea = new Rect.fromLTRB(-offsetX / scaleX,
+                                     -offsetY / scaleY,
+                                     systemWidth + offsetX / scaleX,
+                                     systemHeight + offsetY / scaleY);
+
     _transformMatrix.translate(offsetX, offsetY);
     _transformMatrix.scale(scaleX, scaleY);
-
-    return _transformMatrix;
   }
 
   void _invalidateTransformMatrix() {
+    _visibleArea = null;
     _transformMatrix = null;
     _rootNode._invalidateToBoxTransformMatrix();
   }
@@ -304,7 +321,8 @@
   }
 
   void _tick(double timeStamp) {
-    if (!attached) return;
+    if (!attached)
+      return;
 
       // Calculate the time between frames in seconds
     if (_lastTimeStamp == null) _lastTimeStamp = timeStamp;
@@ -317,7 +335,8 @@
     _frameRate = 1.0/delta;
 
     // Print frame rate
-    if (_numFrames % 60 == 0) print("delta: $delta fps: $_frameRate");
+    if (_numFrames % 60 == 0)
+      print("delta: $delta fps: $_frameRate");
 
     _runActions(_rootNode, delta);
     _callUpdate(_rootNode, delta);
diff --git a/packages/flutter/example/game/main.dart b/packages/flutter/example/game/main.dart
index 8cd364d..70bda87 100644
--- a/packages/flutter/example/game/main.dart
+++ b/packages/flutter/example/game/main.dart
@@ -51,6 +51,7 @@
 class GameDemoApp extends App {
 
   NavigationState _navigationState;
+  GameDemoWorld _game;
 
   void initState() {
     _navigationState = new NavigationState([
@@ -84,21 +85,18 @@
   }
 
   Widget _buildGameScene(navigator, route) {
-    return new SpriteWidget(
-      new GameDemoWorld(_app, navigator, _loader, _spriteSheet)
-    );
+    return new SpriteWidget(_game);
   }
 
   Widget _buildMainScene(navigator, route) {
     return new Center(
       child: new RaisedButton(
         child: new Text("Play"),
-        onPressed: () => navigator.pushNamed('/game')
+        onPressed: () {
+          _game = new GameDemoWorld(_app, navigator, _loader, _spriteSheet, _spriteSheetUI);
+          navigator.pushNamed('/game');
+        }
       )
     );
   }
 }
-
-void resetGame() {
-  _app.scheduleBuild();
-}