9 Replies Latest reply on Sep 10, 2012 2:42 AM by BBlommers

    Performance slowdown when displaying many small images

    faKastner

      Hey!

       

      I'm working on a small Flex Mobile game. It uses a 2D game grid and I am trying to display tiles in the cells. There is 3 different images being used: One for Walls, one for floor and one for the character.

       

      I display these images in the following way:

      I have a group with a id ( displayGroup ), I then create a array of new Image() with the position they should be placed at and the image they should use.

      I then loop over the array and add the images individually as a element into the group.

       

      In code, it looks like this:

       

      var temparray:Array = disp.drawMapGfx(map, hero)
      for each (var img:Image in temparray) 
      {
          displayGroup.addElement(img);
      }
      

       

      Now, the problem appears when I move.

      What I currently do is the following:

       

      var numImages:int = displayGroup.numElements-1;
      
      var temparray:Array = disp.drawMapGfx(map, hero)
      for each (var img:Image in temparray) 
      {
          displayGroup.addElement(img);
      }
      
      
      for(var i:int = numImages; i >= 1; i--)
      {
          displayGroup.removeElementAt(i);
      }
      

       

      I first grab the total number of images in the group. Then I add the updated images that take the new position into account, and then I remove all the outdated images from the group. I go to 1 instead of 0 because element 1 is a rect that creates the background color.

       

      For testing, I've been using "Run as iPhone 3 GS". Every image is 50x30, which, taking the GUI into consideration equates to 11 x 17 images + 1 for the character. So it is displaying up to a maximum of 188 images.

       

      Picture for reference:

      mobile_imageproblem.png

      The problem is that the images flicker as the old images disappear and the new images appear. What can I do to have it redraw the screen faster, to prevent any flickering ?

      Would it make sense to, instead of redrawing every image, to simply shift the whole scene and just load / remove images where necessary, rather than reloading all images ?

       

      And a small second question: Is it possible to share a mobile app in a single desktop-ready AIR file, so that I can have friends who do not have smartphones help test on their computer ?

        • 1. Re: Performance slowdown when displaying many small images
          BBlommers

          How big is the world, in total?

          Theoretically, you could load the whole world on startup, and use a mask to display only the relevant part of the world. With each move of the character, you move the mask to the next section.

          • 2. Re: Performance slowdown when displaying many small images
            faKastner Level 1

            Hello,

             

            Thank you for replying, BBlommers,

             

            the world in total is 80 by 24 cells, so not impossibly large, though trying to load all images at once without any mask or anything does spike the memory usage to 20 MB ( currently, it sits at around 6 MB ).

             

            How does this masking that you speak of work? And would embedding the images make sense ?

             

            I've played around a bit with re-using the same image containers and setting clearOnLoad to false, the result is better, but when it switches from the invisible black tiles to display, there is still flickering.

             

            Currently, I'm thinking that maybe if I expand the view to around 2 cells beyond visible range, as a buffer, and then copy the source of one image into the other ( e.g. image[0].source = image[12].bitMapData , i might be able to prevent flickering, but I still have to test that.

             

            Any ideas, tips or advice in the meantime would be really helpful.

            • 3. Re: Performance slowdown when displaying many small images
              BBlommers Level 2

              Embedding the images will probably improve the load time (and improve the memory usage as well).

               

              By masking, you 'mask' a part of the image. In other words, you only display a specific rectange of the image. You can think of it as a viewport; only the part that is masked will be visible.

               

              Example code:

               

              <fx:Script>

              public function move():void {

                   moveCanvas.xTo = 100;

                   moveCanvas.play();

              }

              </fx:Script>

               

              <fx:Declarations>

                      <s:Move id="moveCanvas" target="{panel1}" duration="500" />

                  </fx:Declarations>

               

                  <!-- Surrounding group necessary, since panel1 shouldn't have an HorizontalLayout as parent -->

                  <s:Group id="surroundingGroup" width="100" right="0">

               

                      <s:Group id="mask" top="0" right="0" bottom="0" left="0">

                          <s:Rect width="100" height="100">

                              <s:fill>

                                  <s:SolidColor color="0xFF0000" />

                              </s:fill>

                          </s:Rect>

                      </s:Group>

               

                      <s:Group id="panel1" top="5" width="100" mask="{mask}" maskType="clip">

                          <CellContainingImage width="100" />

                          <CellContainingImage width="100" left="100" />

                      </s:Group>

              </s:Group>

               

              Panel1 is the total world view, containing all cells. By calling the move-method, you move this world to the left. The viewport (mask) stays in place, but the world behind this viewport moves to the left. This way, you would see the cell to the right of the current cell.

              I hope this makes sense.. Let me know if there are any issues.

              1 person found this helpful
              • 4. Re: Performance slowdown when displaying many small images
                faKastner Level 1

                Hello,

                 

                I've tried the embedding and it's helped a lot, but it's not perfect yet, so I will definitely look into the masking you suggested.

                 

                I figured that other people might have smiliar issues so I wanted to post some stats I've gathered on the different implementations.

                 

                I've tested 3 different implementations:

                 

                Implementation 1: Simply using the images location as the source of the image tag:

                 

                package model
                {
                    import mx.core.BitmapAsset;
                    import mx.core.FlexGlobals;
                
                    import spark.components.Group;
                    import spark.components.Image;
                
                    public class Display
                    {
                
                        private var floorURI:String = "img/Brown-Block.png";
                
                     < insert unimportant code >
                
                     function that creates the image tags {
                          var image:Image;
                          image = new Image();
                          image.clearOnLoad = false;
                          image.x = imgOffsetX + (j-base_x) * imgSizeX;
                          image.y = imgOffsetY + (i-base_y) * imgSizeY;
                          image.source = floorURI;
                          group.addElement(image);
                     }
                

                 

                This resulted in a peak memory usage of 9.6 MB, and had considerable flickering.

                 

                Next test was a poor implementation of embedding. I will post it here in shame, so that other people don't make the same mistake

                 

                package model
                {
                    import mx.core.BitmapAsset;
                    import mx.core.FlexGlobals;
                
                    import spark.components.Group;
                    import spark.components.Image;
                
                    public class Display
                    {
                          [Embed(source="img/Brown-Block.png")]
                          private var floorURI:Class;
                
                     < insert unimportant code >
                
                     function that creates the image tags {
                          var image:Image;
                          image = new Image();
                          image.clearOnLoad = false;
                          image.x = imgOffsetX + (j-base_x) * imgSizeX;
                          image.y = imgOffsetY + (i-base_y) * imgSizeY;
                          image.source = new floorURI() as BitmapAsset;
                          group.addElement(image);
                     }
                

                Needless to say, making a new class for every image did no favors to the memory usage, though the loading time of the images still improved considerably.

                Peak memory usage of 32.3 MB

                 

                Now, finally, what I assume to be the prim and proper implementation , and the one I should've used from the beginning:

                 

                package model
                {
                    import mx.core.BitmapAsset;
                    import mx.core.FlexGlobals;
                
                    import spark.components.Group;
                    import spark.components.Image;
                
                    public class Display
                    {
                          [Embed(source="img/Brown-Block.png")]
                          private var floorClass:Class;
                
                          private var floorURI:BitmapAsset = new floorClass() as BitmapAsset;
                
                     < insert unimportant code >
                
                     function that creates the image tags {
                          var image:Image;
                          image = new Image();
                          image.clearOnLoad = false;
                          image.x = imgOffsetX + (j-base_x) * imgSizeX;
                          image.y = imgOffsetY + (i-base_y) * imgSizeY;
                          image.source = floorURI();
                          group.addElement(image);
                     }
                

                Just a single embedded class turned into a ressource, which is then reused by every image as it's source ( which is OK as we won't be modifying or applying any effects to it ).

                A peak memory usage of 6.2 MB AND fast load times.

                 

                Now to try the masking !

                • 6. Re: Performance slowdown when displaying many small images
                  faKastner Level 1

                  Hello,

                   

                  @Flex harUI : The spritesheet thing unfortunately doesn't seem like it was related to my problem. I don't use a 3d engine, and condensing 3 files into one wouldn't give me a sizable performance increase, but thank you for trying!

                   

                  @BBlommers: Thank you very much for the mask suggestion!

                   

                  I ended up going with a mix between your suggestion and what I had thought of myself: I use the mask, but instead of loading the whole world, I simply load 1 cell outside of the player's view as a buffer. When the player moves, I move the mask, then load in the next row of images just outside of the mask, and then removing the row of images on the opposite site, so that it maintains a 1 cell buffer on each side.

                  This results in a fairly low memoy usage ( 13 MB peak when moving rapidly, but mostly sitting at 10 MB ) and absolutely no flickering, as the images that come into view have been loaded already.

                   

                  I've marked your initial response where you suggested to use a mask as correct. Thank you very much for your assistance!

                  • 8. Re: Performance slowdown when displaying many small images
                    faKastner Level 1

                    Hello again BBLommers, I had 2 follow up questions to your (awesome!) solution.

                     

                    The major one is the following:

                    I added multiple masked groups to use as different layers for my graphical assets.

                     

                    What I mean is that I have one group soley for the map's terrain, called the mapLayer

                    One group soley for the units populating the map, called the unitLayer

                    One group for objects ( chests, barrels and what have you ) populating the map called the objectLayer

                    One group for items, called the itemLayer

                    and finally a group for text that gets displayed called the labelLayer.

                     

                    In code, it looks like this:

                     

                    <s:Group id="surroundingGroup" width="100%" height="100%" clipAndEnableScrolling="true">
                        <s:Group id="mask" width="100%" height="100%">
                            <s:Rect width="100%" height="100%">
                                <s:fill>
                                    <s:SolidColor color="0x000000" />
                                </s:fill>
                            </s:Rect>
                        </s:Group>
                    
                        <s:Group id="mapLayer" width="100%" height="100%"
                                 color="#000000"  fontFamily="Courier New"
                                 fontSize="15" fontStyle="normal" fontWeight="normal"
                                 lineThrough="false" 
                                 textAlign="center" textDecoration="none" lineHeight="112%"
                                 mask="{mask}" maskType="clip">
                        </s:Group>
                        
                        <s:Group id="objectLayer" width="100%" height="100%" mask="{mask}" maskType="clip" />    
                        <s:Group id="itemLayer" width="100%" height="100%" mask="{mask}" maskType="clip" />    
                        <s:Group id="unitLayer" width="100%" height="100%" mask="{mask}" maskType="clip" />    
                        <s:Group id="labelLayer" width="100%" height="100%" mask="{mask}" maskType="clip" />
                        </s:Group>
                    </s:Group>
                    

                     

                    Previously, I had it all in a single group, but it became very cumbersome to work with as it was highly unstable - say because of a logic error a early element gets removed, which gets the index of what element has what ID in the group mixed up, which then quickly results in out of range errors.

                     

                    This new approach is alot easier and faster to work with, not to mention more stable, but the problem comes when working with depth. As the tiles I am using have a perspective to them, I need some to be at a higher Z-level , or depth, than others. Previously I was able to easilly solve this by assinging the depth = the Y position on the coordinate grid of the unit in question, but now that I use individual groups, it seems impossible to align them on the same depth.

                     

                    A picture might be helpful:

                    This is how I want it to be ( how it was with my old implementation )

                    mobilegame_03.png

                    Please note how one of the ladybugs is partially hidden by the wall.

                    Now with the new method of having multiple groups:

                    mobile_depthproblem.png

                    The ladybug always appears unobscured, due to being in a different group.

                     

                    So the question is: Is there a reasonable way to get the desired result ? Or is there a better approach, generally speaking to be able to somewhat separate the different elements?

                     

                    The second question is much simpler:

                    I get a bunch of warnings about mask not being bindable, but I'm not sure in what way I would bind it to get rid of the error. I don't really intend on ever changing the mask as it's simply the canvas. I've tried to look up information on how to bind spark elements such as the group I am using for a mask, but came up blank. ( Or the answers I found just raised way more questions on how it translates to my situation ).

                     

                    If you'd prefer that I make a new topic then I will gladly do so, for now I put it here as it relates.

                    • 9. Re: Performance slowdown when displaying many small images
                      BBlommers Level 2

                      Apologies for the late response, I was on holiday.

                       

                      Is the problem already solved, or are you still stuck? I don't have a problem at hand, but I can give it some thought if it's still relevant.

                       

                      As for the second question, I encounter the same warnings, but have never acted on it. I don't think it is related to the first question, or is reponsible for any error at all.