Thursday, February 14, 2013

Firefly Part 4: Layouting system

In our previous posts we created game project with basic navigation flow and texture management. Now time to a little bit more complicated things - layouting system. URL for showcase project is provided at the bottom of this post, you can download it and import as a project into your FlashBuilder.
When we were creating layouts we had in minds two things: it should be flexible, easy to use and works well across large variety of different screen resolutions.

System has five types of layout measure units:
  1. px - absolute value in pixel (defaul units if not specified).
  2. pct - persentage from size of parent container.
  3. inch - absolute value in inches, it takes into account screen DPI.
  4. acpx - absolute context pixel, it depends on container layout context, by default it design pixel shifted by aligment (will explain later).
  5. rcpx - the same as acpx but without shifting by aligment (will explain later).
All layout constrains starts from symbol "$" to make them easy to find. Ok, let's change LogoSplash to make logo positioned on the center:
// Add Company logo
addElement(new CompanyLogo(), $hCenter(0), $vCenter(0), $vRatio(50).pct);
$vRatio(50).pct means to set size of 50% from size of parent height keeping aspect ratio of object in our case logo image. Now we can add buttons and labels into MenuView. For example we want to add score label to be positioned just on the black second layer of background, but on different devices this layer will be in different places (see details in this post). Here the place where "acpx" units needed, you need just to set value on design image in our cases design size 1024x768 so we need just take pixel value from Illustrator and this value will be automatically converted into pixels relatively to current mobile device. And also we use bottom aligment to crop background so we need to specify it in our layout context:
// Set default menu view layout context aligment as BOTTOM
layoutContext = new BasicLayoutContext(this, HAlign.CENTER, VAlign.BOTTOM);

// Best Score Message
var bestScoreMessage:TextField = new TextField("Best Score", "Chango", 80, 
                                               0x00FF00);
bestScoreMessage.touchable = false;
bestScoreMessage.autoScale = true;
bestScoreMessage.hAlign = HAlign.LEFT;
bestScoreMessage.vAlign = VAlign.TOP;
addElement(bestScoreMessage, $right(86).acpx, $top(540).acpx, 
           $width(180).acpx, $height(50).acpx);
Layout units rcpx means the same but it doesn't shift by aligment. For now there are following layout constrains:
  • $x
  • $y
  • $width
  • $height
  • $top
  • $bottom
  • $left
  • $right
  • $vCenter
  • $hCenter
  • $vRatio
  • $hRatio
  • $fit
So now we can add the rest elements into our views.

Showcase project source: ZombieRisingUp Part 4

46 comments:

  1. Can u Integrate CitrusEngine Physics Libraries into this, it will be fantastic toolkit

    ReplyDelete
  2. It's a very interesting mechanism, but while implementing it I'm facing a problem with tweening x/y position (via Starling juggler), since you ca't modify those variable directly. What would be the best way to do that then?

    ReplyDelete
    Replies
    1. I guess your are trying to animate with regular Tween class, instead of this you can animate using com.in4ray.gaming.effects.Move animation class which is actually wrapping Tween.

      Delete
    2. Thanks for the quick answer, it's working great using that wrapper! Excellent job with the SDK by the way, i quite enjoy developing with it :)

      Delete
  3. Hi guys,

    I'm trying to use this system in a Starling/Citrus Engine project.

    I have a FireFly Sprite which contains a FireFly Button.

    I add the button this way :

    //layoutContext = new BasicLayoutContext(this, HAlign.CENTER, VAlign.BOTTOM);

    addElement(btnNext,$hCenter(0),$vCenter(0));

    But when I launch my game I have this error message :

    Error: Forbidden, use layouts
    at com.in4ray.gaming.components::Sprite/set x()[D:\Projects\in4ray\sdk\src\com\in4ray\gaming\components\Sprite.as:176]

    Is it possible to use a FireFly components in a Starling/CitrusEngine project ? I really want to use your layout/addElement system which is very useful !

    Thank you

    ReplyDelete
    Replies
    1. It's because somewhere you are trying to set x property on Firefly Sprite component. If you want to set position or size of component without layouting system you should use setActualPosition(x,y) and setActualSize(w,h) methods.

      Delete
    2. Ok, I get it ! I will post in the Citrus Engine forum about it because the coordinate modifications it's done by one of its framework class !

      Question : Could you plan to "extract" those functions in an utils class ? I mean, the ability to compute coordinate the way is done in your Layout system is really great. Have the ability to call a function like addElement just to compute coordinates would be great.

      Thank you

      Delete
    3. Especially for you I've added ability to calculate layouting values on mockup object. Usage:

      var m:LayoutMock = new LayoutMock(this);

      m.addLayout($x(10).acpx, $y(40).pct, $width(2).inch, $height(30).rcpx).layout();

      trace("x: " + m.x + " y: " + m.y + " w: " + m.width + " h: " + m.height);

      Delete
    4. I tried your example but the application crashed. The error message is the following :

      TypeError: Error #1009: Cannot access a property or method of a null object reference.
      at com.in4ray.gaming.layouts.context::BasicLayoutContext/get designScaleFactor()[C:\Users\vstyran\git\firefly-sdk\firefly\src\com\in4ray\gaming\layouts\context\BasicLayoutContext.as:218]
      at com.in4ray.gaming.layouts.context::BasicLayoutContext/getValueXInternal()[C:\Users\vstyran\git\firefly-sdk\firefly\src\com\in4ray\gaming\layouts\context\BasicLayoutContext.as:95]
      at com.in4ray.gaming.layouts.context::BasicLayoutContext/getValueX()[C:\Users\vstyran\git\firefly-sdk\firefly\src\com\in4ray\gaming\layouts\context\BasicLayoutContext.as:74]
      at LayoutX/layout()[C:\Users\vstyran\git\firefly-sdk\firefly\src\com\in4ray\gaming\layouts\$x.as:46]
      at com.in4ray.gaming.layouts::LayoutManager/layout()[C:\Users\vstyran\git\firefly-sdk\firefly\src\com\in4ray\gaming\layouts\LayoutManager.as:49]


      Do I have to set something in particular ?

      Delete
    5. What do you pass as a constructor argument for new LayoutMock(this) ?

      Delete
    6. The code is inside a FireFly Sprite so the argument is 'this' so is a FireFly Sprite.

      Is it ok ?

      Delete
    7. yes it should be FireFly Sprite. Strange, can you send fragment of code?

      Delete
    8. Here the code :

      package views.dialog
      {
      import com.in4ray.gaming.components.Sprite;
      import com.in4ray.gaming.layouts.$height;
      import com.in4ray.gaming.layouts.$width;
      import com.in4ray.gaming.layouts.$x;
      import com.in4ray.gaming.layouts.$y;
      import com.in4ray.gaming.layouts.LayoutMock;

      import starling.events.Event;

      public class DialogView extends com.in4ray.gaming.components.Sprite
      {

      public function DialogView()
      {
      super();

      addEventListener(Event.ADDED_TO_STAGE,onAddedToStage);
      }


      private function onAddedToStage():void
      {
      var m:LayoutMock = new LayoutMock(this);

      m.addLayout($x(10).acpx, $y(40).pct, $width(2).inch, $height(30).rcpx).layout();

      trace("x: " + m.x + " y: " + m.y + " w: " + m.width + " h: " + m.height);

      }

      //Ovveride to prevent direct access to x,y, width and height
      override public function set x(value:Number):void{

      setActualPosition(value,y)
      }

      override public function set y(value:Number):void{

      setActualPosition(x,value)
      }

      override public function set width(value:Number):void{

      setActualSize(value,height);
      }

      override public function set height(value:Number):void{

      setActualSize(width,value);
      }
      }
      }

      Delete
    9. it works for me. Make sure that your main class extends GameApplication and that you specify design size, example:

      setDesignSize(1024, 768);
      setDesignDPI(240);

      Delete
    10. Ok, I get it. My main class doest not extends GameApplication. Actually I'm trying to use part of Firefly framework in a CitrusEngine/Starling project. I thought it could works because instead of using Starling Sprite I would just have to use Firefly Sprite.

      I'm not sure I will be able to use GameApplication class in such type of project (CE + Starling) but in bother me because really want to use you layout system or at least be able to compute coordinates like in layout. That's why I asked if it could be possible to "extract" those functions. But maybe it's not easy.

      Thanks for your help !

      Delete
    11. One last question ... I hope ;-)

      I have changed the type of my main class so your example works fine. But it seems that I can't use all available function. For example, if I change your example :

      m.addLayout($x(10).acpx, $y(40).pct, $width(2).inch, $height(30).rcpx).layout();

      by

      m.addLayout($x(10).pct, $y(40).pct, $width(2).inch, $height(30).rcpx).layout();

      I have this error :

      ArgumentError: Object not connected to target
      at starling.display::DisplayObject/getTransformationMatrix()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/libs/starling_master/src/starling/display/DisplayObject.as:231]
      at starling.display::Quad/getBounds()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/libs/starling_master/src/starling/display/Quad.as:93]
      at starling.display::DisplayObjectContainer/getBounds()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/libs/starling_master/src/starling/display/DisplayObjectContainer.as:281]
      at starling.display::Sprite/getBounds()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/libs/starling_master/src/starling/display/Sprite.as:161]
      at parallax::ParallaxEngine/update()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/src/parallax/ParallaxEngine.as:49]
      at citrus.core::MediatorState/update()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/libs/citrus_master/src/citrus/core/MediatorState.as:81]
      at citrus.core.starling::StarlingState/update()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/libs/citrus_master/src/citrus/core/starling/StarlingState.as:68]
      at states.levels::GameLevelState/update()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/src/states/levels/GameLevelState.as:310]
      at citrus.core::CitrusEngine/handleEnterFrame()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/libs/citrus_master/src/citrus/core/CitrusEngine.as:238]
      at citrus.core.starling::StarlingCitrusEngine/handleEnterFrame()[/Users/sebastien/Documents/Adobe Flash Builder 4.7/mathMathews2/libs/citrus_master/src/citrus/core/starling/StarlingCitrusEngine.as:0]

      I tried with some other arguments like $vCenter or $hCenter etc. and I always has the same error.

      Any idea ?

      ps: sorry to bother you but I feel I'm close to succeed ;-)

      pps : thank you

      Delete
    12. Oh I see, here some hack for you:

      private function onAddedToStage():void
      {
      GameGlobals.gp_internal::_stageSize = new Point(stage.stageWidth, stage.stageHeight);
      GameGlobals.gp_internal::_dpi = Number(unescape(Capabilities.serverString).split("&DP=", 2)[1]);
      GameGlobals.gp_internal::_designSize = new Point(1024, 768);
      GameGlobals.gp_internal::_designDPI = 240;

      var m:LayoutMock = new LayoutMock(this);
      m.addLayout($x(10).acpx, $y(40).pct, $width(2).inch, $height(30).rcpx).layout();
      trace("x: " + m.x + " y: " + m.y + " w: " + m.width + " h: " + m.height);
      }

      Delete
    13. My last comment was to your previous one, this hack alows you to not use GameApplication class.

      Delete
    14. I will try this right now !

      thanks

      Delete
    15. Ha ok ! ...

      Any idea for my other post ?

      Delete
    16. After this hack and the fact that you are not using GameApplication anymore the issue remains?

      Delete
    17. Hi,

      Now it works, thank you !

      One question about the usage. I want in my class to compute coordinates for example center some elements. So I tried something like :

      var m:LayoutMock = new LayoutMock(this);
      m.addLayout($hCenter(0),$vCenter(0)).layout();

      But then the x and y values don't match with the center of the screen. I tried to use "pct" but the value remains wrong.

      May be I have misunderstand the usage ?

      Again, thank you !

      Delete
    18. If you want to set component on the center you need:
      1. Make sure that container has width and height non 0
      2. Add size to layout target ($width, $height) because calculation is following x = (containerWidth - targetWidth)/2.
      3. Make sure that pivotX and pivotY set to 0 for layout target (for now all layout calculations are made assuming that pivots are 0).

      Delete
  4. Ho that's great !

    Thank you very much !

    ReplyDelete
  5. Hi,
    Please help on iphone 5, How can i fit the .fxg on screen. Only works on iphone and ipad.

    Thanks

    ReplyDelete
  6. Hi,

    I mean works on iphone 4 and ipad not on iphone 5. Please help.

    Thanks

    ReplyDelete
  7. I've got two FXGs the same size, but when I center them (even swapping them into the same container) they are not in the same spot. Any idea how that can happen?

    ReplyDelete
    Replies
    1. Hi, make sure you set size of your container. By default container in Starling takes size from it's children and vCenter/hCenter layouts can show strange results.

      Delete
  8. How do I make a vertical scrolling list of item renderers? The navigation map looks close, although I don't want it item centric like a carousel, just want a bunch of items with a small gap between them. The Feathers List looks like it's perfect, but I can't get that to work in your game SDK. Do you have a vertical scroll container or is there a way to make the feathers List work in your game framework?

    ReplyDelete
    Replies
    1. I think your framework is a nice start but it's got a long way to go. I have tried using it for over a week and it's just missing too much. I'm going to switch back to feathers.

      Delete
    2. Hi, Thanks for feedback. We are working on new version which we think will be more usable, understandable and easy to start.

      Delete
    3. Cool, I'll keep an eye out for it. I think you've got some really cool things, especially the FXG to texture library... very nice.

      But, one of the things that makes it difficult to use right now is that the SWCs are not versioned. So, the docs don't match your current release version, nor do they match the swcs that are in your demo projects/templates. So, it means a lot of code digging to figure out what's going on and how to use certain APIs.

      Having a good built in way to use textures in item renderers would be a nice feature.

      Other than that, I did end up building a vertical scroll component based on your navigation map... but it functions more like a typical scroll component than being "screen" centric. It performs really well, looks native, but it's got some dirty hacks in it right now -- I'll contribute it once it's been fully abstracted.

      That said, I started by using the swc from PortraitGameTemplate4 and it's not versioned, and it appears to be different from both the version 1.0 and 1.1 swcs so I really don't know what version it is or if my component will be compatible with other versions. I tried swapping it with both the v1.0 and v1.1 swcs and it broke a lot of code.

      If you guys could version your swcs that would be really awesome! Example: firefly-sdk-1.3.123.swc

      Delete
    4. There is no release tag 1.1, there is branch 1.1 which is development branch for new completely reworked version of SDK that currently under development and not ready to use. So do not use it for now.

      Delete
    5. Fair enough. But even the SWC in the template I was using didn't seem to match any of the releases or branches I was able to download. It seemed close to 1.0, but not the same.

      Delete
    6. How does one add a PNG texture to the firefly sprite as a child or element? FXGs are working great, but can't figure out how to use a PNG!

      Delete
    7. Currently Firefly SDK 1.0 hasn'r support bitmap textures (PNG, JPEG etc.). But we are working under version 1.1 where we are going to support bitmap textures (PNG, JPEG), FXG textures, ATF textures and Dragon Bones textures. All work done you can find in our branch on Github.

      Delete
  9. When using this layout system for buttons and such, how can I animate something else across the screen? Is that possible, or am I stuck with static objects that cannot move?

    ReplyDelete
    Replies
    1. Can items be added to the display that move/do not conform to this layout or am I stuck with static objects?

      Delete
    2. No, they aren't static objects. You can animate them using our implementation of effects ("com.in4ray.gaming.effects" package). As for moving animation please use "Move2" implementation instead of "Move" for better performance.

      Delete
  10. How does the Move2 class work? For example, can you give code example of how to set the value of fromX? It says it needs to be of type ILayout, and the only class I saw that implements that is LayoutBase so I made an instance of that but it doesn't seem to work. Can you provide a example of how to set the FromX property on the Move2 class?

    ReplyDelete
    Replies
    1. Figured it out, here it is for those who also wonder:

      var moveObj:Move2 = new Move2( image, 300, $x(0).px, $y(h0).px,$x(0).px, $y(25).px);

      Delete
  11. If I wanted to give the user the ability to drag an item on the screen, how can I do that? Whenever I set the x/y values it tells me to use layouts, and layouts nor Move2 seem like the right way to do it either. What is best?

    ReplyDelete
    Replies
    1. Or you can use addLayout($x(0), $y(0)).layout()

      Delete
  12. Hi, you can use setActualPosition(x,y)

    ReplyDelete
  13. I do it so:
    case TouchPhase.MOVED:
    _deltaMovement = touch.getMovement(_gameViewMap);
    _gameViewMap.transformationMatrix.translate(_deltaMovement.x, _deltaMovement.y);
    break;

    ReplyDelete