About a week ago I jumped into a discussion on Flashkit about AS3 sprite sheets, rendering, etc. There were a lot of good ideas being thrown around, and I put in my 2 cents on how my current engine works. Originally, back in the AS2 days (and early days of this blog), I did some tests with my original BitmapData caching engines and reported some of the results in a blog entry called Retro Blaster is Coming. In it, I detail how I used an Array of BitmapData objects in place of gotoAndPlay and achieved some very nice results. When I started to create a new game engine in AS3, I consulted Chris Cutler, who is an excellent Flash Game programmer and my partner in crime at my day job. He had created a fully blitted side scrolling engine for the Pixel Chix web site in AS2. After consulting him on his basic methodology, I set out to create my own version is AS3. These were the principals I wanted to follow:
1. I will display ALL game objects (including any text that occupies the same area as sprites and tiles) on ONE canvas. This would require the manipulation and blitting of BitmapData for all game objects and a copyPixels operation to update them to the screen.
2. I would hold all of the sprite sheets in arrays of BitmapData for ease and speed. I call these virtual sprite sheets.
3. I would use the new event model to fire off render events to my game objects.
With those goals in mind, I created my new game engine and the forth-coming game, Pumpkin Man to test it. The engine has been working pretty well for me, but as soon as I participated in the Flashkit discussion I began to wonder just how my current engine compares to other engines. Since this is the most optimized engine I have ever created, I dropped my game development for a few days and began to code up a way to test both screen render speed (using FPS as my units) and a method to calculate the length of time the Event model was taking compared to a straight method call to update objects.
I needed to bombard Squize (from Flashkit and the Gamingyourway.com site), as well as Chris Cutler, Jobe Maker, and Mike Grundvig to find any new ideas for rendering the screen. I discovered two methods I had not tried, so I added them to my test. The first method was to replace the Array that holds the Bitmapdata objects with a single sprite sheet strip. Each game object would be an individual sprite (as opposed to blitting the all to a single sprite). I would use the scrollrect property of the Bitmap object inside the sprite to control the animation. It turned out to not be such a hot idea. I can’t blame any of those above for this method because I just made it up after talking to them. It seems that the individual sprites really slow the render throughput, and the scrollrect seems to be painfully slow given that is had to work on so many individual sprites at the same time. I tried and failed to create a version of this with one shared Bitmap (as to only have to update one scrollrect property) but when I attempted to copyPixels from it, I always got the first frame of animation.
After hitting up Squize a few more times, I came to realize that he was using scrollrect to actually only animate ONE sprite, that being the background and play areas of his great new Geometry Wars-like game (he let me preview it). So, it seems that I was using the scroll rect in a completely wrong manner. After talking to Mike Grundvig, I came to realize, that I needed to figure out a whole new way to attempt to scrollrect through a sprite sheet. What I came up was using the same sprite sheet strip instead of an array, but to still blit them all to a single sprite by changing the copyPixels rect (not the scrollrect property) on each frame to hover over just the bitmap I needed.
Anyway, after finally getting a solid foundation in the 5 different rendering engines I wanted to test, I coded up a Flash App that can be used to test and get the results for different combinations of object counts, rendering methods, and what I call models: using either and event or a method to update objects. In this way I would be able to tell which of my 5 different methods was the best. As an aside, even though I got some great results in some of my tests, I have not tested any of these against other people’s rendering engines, so I have no no idea if these are the 5 best choices to test, but they are 5 of my methods. I am sure there are better methods out there.
Method # 1: Animate with an array. Output to one sprite.
This was my current engine of choice when I began this test. The code for the render() method looks like this:
[cc lang=”javascript” width=”550″]
if (animationFrameCount > animationFrameDelay) {
spriteBitmapDataIndex++;
if (spriteBitmapDataIndex == aSpriteBitmapData.length) {
spriteBitmapDataIndex=0;
}
animationFrameCount=0;
}
animationFrameCount++;
blitPoint.x=x;
blitPoint.y=y;
parentClass.getScreenBD().copyPixels(aSpriteBitmapData[spriteBitmapDataIndex],blitRectangle, blitPoint);
[/cc]
Basically, each game object runs this method to jump to the next frame of animation. The array of BitmapData that makes up this virtual sprite sheet is called aSpriteBitmapData. The index of that array is updated only after animationFrameDelay has passed since the last animation frame change. The blitPoint is always the current x,y of the object, and the blitRectangle is always just this: blitRectangle=new Rectangle(0, 0, 32, 32); All objects blit to a single BitmapData called : screenBD (in my parent SpeedTest class);
Up until now, I had assumed that this would be the fastest method I could come up with.
Model # 2: Animate with an array. Output to individual sprites
The only real conceptual difference between this and #1 is how the game objects are rendered on the screen. I still use a virtual sprite sheet Array of BitmapData to hold the animation. I just have each game object render itself as a sprite to the screen instead of blitting to one BitmapData object. This gives me more control over the the Sprites and allows me to make use of Sprite properties such as rotation and scale is needed. The code for the render() function looks like this:
[cc lang=”javascript” width=”550″]if (animationFrameCount > animationFrameDelay) {
spriteBitmapDataIndex++;
if (spriteBitmapDataIndex == aSpriteBitmapData.length) {
spriteBitmapDataIndex=0;
}
animationFrameCount=0;
}
animationFrameCount++;
displayBitmap.bitmapData=aSpriteBitmapData[spriteBitmapDataIndex];
[/cc]
Instead of blitting to a single BitmapData object, I simply update the BitmapData property of the Bitmap used to display the object on the screen: displayBitmap.bitmapData=aSpriteBitmapData[spriteBitmapDataIndex]; This animation Array is handled the same as above in Model #1. This is essentially the the render model I built in AS2, only in AS2/Flash 8 I used the attachBitmap method of a MovieClip as opposed to changing the reference to the BitmapData on a Bitmap object.
Model# 3: Animate with scrollrect. Output to individual sprites.
This method makes use of a single BitmapData strip inside a Display Bitmap instead of a virtual sprite sheet in an Array. It is a literal sprite sheet. On each frame update the scrollRect property of the Bitmap is updated to the needed positron and it acts like a MASK, just showing the part of the sheet I want for that particular frame.
[cc lang=”javascript” width=”550″]if (animationFrameCount > animationFrameDelay) {
spriteBitmapDataIndex++;
if (spriteBitmapDataIndex == spriteSheetFrameCount) {
spriteBitmapDataIndex=0;
}
animationFrameCount=0;
}
animationFrameCount++;
scrollRectangle.x=32*spriteBitmapDataIndex;
displayBitmap.scrollRect = scrollRectangle;
[/cc]
I had high hopes for this one, but it didn’t turn out as expected. This was not quite as fast as I thought it would be. We’ll examine the results in detail the later on in this article.
.Model # 4: Animate with GAS. Output to individual sprites.
I did this one on a lark. I was frustrated with the results of my pathetic scrollrect experiment above, so I decide to test it against a basic gotoAndStop implementation. I built a MovieClip in the library with all 8 frames and a stop on each frame. I copied the Bitmaps from my sprite sheet to individual MovieClips and added one to each frame. This gave me basically what I would have had in AS2/Flash 8 before I began to use BitmapData.
[cc lang=”javascript” width=”550″]if (animationFrameCount > animationFrameDelay) {
gasMC.play();
animationFrameCount=0;
}
animationFrameCount++;
[/cc]
The code for this render() is very straightforward, it just play()s to the next frame after the appropriate animationFrameDelay has been reached.
.Model # 5: Animate sprite sheet with rect. Output to one sprite.
This is a combination of #1 and #3. I use ONE BitmapData canvas inside one Sprite to blit to, but instead of an virtual sprite sheet array, I use a real Bitmapdata object in memory as the literal sprite sheet. The rect property of the copyPixels operation is manipulated to achieve the illusion of animation. This the one I came up with after consulting Squize, Cutler, Maker, and Grundvig.
[cc lang=”javascript” width=”550″]if (animationFrameCount > animationFrameDelay) {
spriteBitmapDataIndex++;
if (spriteBitmapDataIndex == spriteSheetFrameCount) {
spriteBitmapDataIndex=0;
}
animationFrameCount=0;
}
animationFrameCount++;
blitRectangle.x=32*spriteBitmapDataIndex;
blitPoint.x=x;
blitPoint.y=y;
parentClass.getScreenBD().copyPixels(parentClass.getSpriteSheet(),blitRectangle, blitPoint);
[/cc]
The blitPoint and blitRectangle must be updated each frame to scroll along the sheet and ensure the data is blitted to the correct position on the screenBD canavas.
RESULTS:
My basic methodology for the test was to make sure the only programs running on my machine were Excel and the external .swf player for CS3. I did all of these tests outside of the Browser and the Flash IDE as to not be encumbered by the limitations of each. I wrote the testing program in a way that allows me to perform multiple tests without shutting down an restarting the program, but because I am sure garbage collection would have played a part in changing some of the results, I opted to close and restart the testing app between each run. Each test was run for 1000 frames.
100 Object Test. Below are my results for the first run with 100 objects animating and moving across and down the screen. I have noted the peak frame rate and render frame rates and model times. The model time is calculated through the use of the older get Timer() method because it is the only timing method I could find that would actually give me accurate results (Thanks for your help on this one, Chris Cutler). For the Method model, I take the get Timer() time before I start the loop through all of the objects and again when the loop is complete. I have optimized this loop to the best of my ability. For the EVENT model, I took the get Timer() before the event dispatch, and took one on the line directly beneath. It seems that Flash does call these in a synchronous manner, so it seems to work fine.
Model | METHOD | EVENT |
100 Objects | ||
Render (frame rate) Peak | FPS | FPS |
#1 Array, One Sprite | 125 | 126 |
#2 Array, Multiple Sprites | 125 | 126 |
#3 Scrollrect, Multiple Sprites | 125 | 125 |
#4 GAS, Muliple Sprites | 109 | 109 |
#5 Sprite Sheet, One Sprite | 126 | 125 |
Render (frame rate) Avg | FPS | FPS |
#1 Array, One Sprite | 124 | 124 |
#2 Array, Multiple Sprites | 124 | 124 |
#3 Scrollrect, Multiple Sprites | 124 | 123 |
#4 GAS, Multiple Sprites | 107 | 107 |
#5 Sprite Sheet, One Sprite | 124 | 124 |
Model Times Peak | milliseconds | milliseconds |
#1 Array, One Sprite | 4 | 11 |
#2 Array, Multiple Sprites | 3 | 10 |
#3 Scrollrect, Multiple Sprites | 11 | 10 |
#4 GAS, Multiple Sprites | 1 | 10 |
#5 Sprite Sheet, One Sprite | 2 | 2 |
Model Times Avg | milliseconds | milliseconds |
#1 Array, One Sprite | 0.958 | 0.978 |
#2 Array, Multiple Sprites | 0.0069 | 0.013 |
#3 Scrollrect, Multiple Sprites | 0.011 | 0.037 |
#4 GAS, Multiple Sprites | 0.0519 | 0.065 |
#5 Sprite Sheet, One Sprite | 0.975 | 0.969 |
Creation Time | milliseconds | milliseconds |
#1 Array, One Sprite | 3 | 3 |
#2 Array, Multiple Sprites | 5 | 7 |
#3 Scrollrect, Multiple Sprites | 6 | 8 |
#4 GAS, Multiple Sprites | 13 | 13 |
#5 Sprite Sheet, One Sprite | 3 | 3 |
Memory Usage | Kilobytes | Kilobytes |
#1 Array, One Sprite | 4747 | 4915 |
#2 Array, Multiple Sprites | 4788 | 5050 |
#3 Scrollrect, Multiple Sprites | 5279 | 5558 |
#4 GAS, Multiple Sprites | 5152 | 5320 |
#5 Sprite Sheet, One Sprite | 4661 | 5001 |
All of the render methods performed very well from an FPS point of view. All of these achieved at least 109 FPS with 100 objects. GAS was the slowest, but all of the others averaged at least 123 FPS no matter which Model was used. It is interesting to note that the average render time for the model was much much lower for the array of sprite versions (especially the event model). The single bitmap blit (#1, and #5) had a much higher average millisecond MODEL run rate. This didn’t seem to effect the frame rate much, as those achieved some of the highest FPS rates.
The average model time difference between Event and Method was was not as significant. In some cases the EVENT was actually a little faster than the METHOD call. What really effected the model execution time was the render() method chosen.
Consistently, object creation time was the close between the two Models, but varied wildly between the different rendering methods, with GAS taking the most time to initially create 100 MovieClip objects. Memory usage
Memory usage was pretty consistent, with the EVENT model versions taking up a little more ram, while not surprisingly, the Sprite Sheet, Single Sprite (Method #5) overall used the least amount of memory (in METHOD model mode).
.500 Object Test. These were conducted in the same manner as the 100 object tests. Note that at 500 objects the scrolled (#3) and GAS (#4) are starting to become unusable.
Model | METHOD | EVENT |
500 Objects | FPS | FPS |
#1 Array, One Sprite | 125 | 125 |
#2 Array, Multiple Sprites | 125 | 124 |
#3 Collect, Multiple Sprites | 21 | 20 |
#4 GAS, Multiple Sprites | 25 | 26 |
#5 Sprite Sheet, One Sprite | 125 | 125 |
Render (frame rate) Avg | FPS | FPS |
#1 Array, One Sprite | 124 | 124 |
#2 Array, Multiple Sprites | 124 | 123 |
#3 Scrollrect, Multiple Sprites | 19 | 18 |
#4 GAS, Multiple Sprites | 22 | 23 |
#5 Sprite Sheet, One Sprite | 124 | 123 |
Model Times Peak | Milliseconds | Milliseconds |
#1 Array, One Sprite | 6 | 14 |
#2 Array, Multiple Sprites | 2 | 13 |
#3 Scrollrect, Multiple Sprites | 103 | 106 |
#4 GAS, Multiple Sprites | 2 | 12 |
#5 Sprite Sheet, One Sprite | 7 | 15 |
Model Times Avg | Milliseconds | Milliseconds |
#1 Array, One Sprite | 3.962 | 4.1 |
#2 Array, Multiple Sprites | 1.015 | 1.162 |
#3 Scrollrect, Multiple Sprites | 1.086 | 1.173 |
#4 GAS, Multiple Sprites | 1 | 1.128 |
#5 Sprite Sheet, One Sprite | 4.028 | 4.585 |
Creation Time | Milliseconds | Milliseconds |
#1 Array, One Sprite | 9 | 16 |
#2 Array, Multiple Sprites | 28 | 36 |
#3 Scrollrect, Multiple Sprites | 29 | 37 |
#4 GAS, Multiple Sprites | 79 | 92 |
#5 Sprite Sheet, One Sprite | 10 | 15 |
Memory Usage | Kilobytes | Kilobytes |
#1 Array, One Sprite | 5058 | 5390 |
#2 Array, Multiple Sprites | 5480 | 5996 |
#3 Scrollrect, Multiple Sprites | 7364 | 9207 |
#4 GAS, Multiple Sprites | 7767 | 8830 |
#5 Sprite Sheet, One Sprite | 5033 | 5308 |
Again we see that the Event Model is slightly slower in execution speed than the Method Model. We also see that the average execution speed for #1 (Array of Bitmap Data, one display object) and #5 (Sprite Sheet and one display object) is the slowest, but did not affect average frame rate. Those were the fastest versions. It is interesting to note that I might actually choose #2 (array of bitmapData Multiple Sprites) if I was going to have about 500 Sprites on the screen because the frame rate is high, and the average execution speed is the lowest of the three that actually performed well. The execution speed difference would help with things like collision detection and other in game calculations. The down side to #2 is object creation time is the highest of the leading three, so the use of a sprite object pool would be absolutely necessary. Memory usage was pretty good across the board for #1, #2, and #5. These seem to be the best FPS models so far.
1000 Object Test. The same methodology as the 100 and 500 tests was used for this test. ScrollRect (#3) and GAS (#4) are becoming pretty much unusable, and the 1000 frame tests for both took quite a few minutes to complete.
Model | METHOD | EVENT |
1000 Objects | ||
Render (frame rate) Peak | FPS | FPS |
#1 Array, One Sprite | 94 | 93 |
#2 Array, Multiple Sprites | 92 | 93 |
#3 Scrollrect, Multiple Sprites | 8 | 8 |
#4 GAS, Multiple Sprites | 11 | 11 |
#5 Sprite Sheet, One Sprite | 93 | 89 |
Render (frame rate) Avg | FPS | FPS |
#1 Array, One Sprite | 93 | 91 |
#2 Array, Multiple Sprites | 91 | 91 |
#3 Scrollrect, Multiple Sprites | 6 | 6 |
#4 GAS, Multiple Sprites | 9 | 9 |
#5 Sprite Sheet, One Sprite | 92 | 87 |
Model Times Peak | Milliseconds | Milliseconds |
#1 Array, One Sprite | 11 | 20 |
#2 Array, Multiple Sprites | 5 | 17 |
#3 Scrollrect, Multiple Sprites | 103 | 134 |
#4 GAS, Multiple Sprites | 4 | 26 |
#5 Sprite Sheet, One Sprite | 12 | 22 |
Model Times Avg | Millisecond | Millisecond |
#1 Array, One Sprite | 8.22 | 8.682 |
#2 Array, Multiple Sprites | 2.959 | 3.066 |
#3 Scrollrect, Multiple Sprites | 2.055 | 2.02 |
#4 GAS, Multiple Sprites | 2.008 | 2.36 |
#5 Sprite Sheet, One Sprite | 8.707 | 9.01 |
Creation Time | Milliseconds | Milliseconds |
#1 Array, One Sprite | 29 | 49 |
#2 Array, Multiple Sprites | 73 | 92 |
#3 Scrollrect, Multiple Sprites | 71 | 107 |
#4 GAS, Multiple Sprites | 155 | 187 |
#5 Sprite Sheet, One Sprite | 29 | 49 |
Memory Usage | Kilobytes | Kilobytes |
#1 Array, One Sprite | 5414 | 6295 |
#2 Array, Multiple Sprites | 6225 | 7061 |
#3 Scrollrect, Multiple Sprites | 11943 | 12173 |
#4 GAS, Multiple Sprites | 13479 | 13037 |
#5 Sprite Sheet, One Sprite | 5369 | 5644 |
Consistently, #1 using a Method loop to update the sprites is the fastest frame-rate wise. The arrays of BitmapData consume a little more memory than #5 (Using a Sprite sheet instead of an Array). Both #1 and #5 take about 4x longer of processor time on average to call the rendering functions, but this is more than made up for with the throughput of blit rendering to a single BitmapData object. Creation time is still under 30 milliseconds for #1, and #5.
#2 (Using an array of BitmapData for animation, but rending to 1000 separate sprites is still a contender here, and in pure FPS, it even slightly beats #5. It has a lower execution time (2.9 milliseconds compared to 8+ for #1 and #5); The only drawback to #2 is the creation time for the 1000 objects. It takes 2x as long to initialize them as #1 and #5. Also You can see that the execution times for 1000 objects using the EVENT model, while slightly slower in all areas, is very competitive. There is no absolute clear winner here because while #2 is slightly slower frame rate-wise, the execution time would make it easier to squeeze in more collision detection and AI to your 1000 objects.
5000 and 10000 Object Tests. I lumped these two tests together because the results are very similar, though obviously the frame rates are going down, and the execution times are going up.
5000 Objects
Model | METHOD | EVENT | ||
5000 / 1000 Objects | 5000 | 5000 | ||
Render (frame rate) Peak | FPS | FPS | ||
#1 Array, One Sprite | 24 | 22 | ||
#2 Array, Multiple Sprites | 18 | 18 | ||
#3 Scrollrect, Multiple Sprites | x | x | ||
#4 GAS, Multiple Sprites | x | x | ||
#5 Sprite Sheet, One Sprite | 22 | 22 | ||
5000 | 5000 | |||
Render (frame rate) Avg | FPS | FPS | FPS | FPS |
#1 Array, One Sprite | 23 | 21 | ||
#2 Array, Multiple Sprites | 17 | 17 | ||
#3 Scrollrect, Multiple Sprites | x | x | ||
#4 GAS, Multiple Sprites | x | x | ||
#5 Sprite Sheet, One Sprite | 22 | 21 | ||
5000 | 5000 | |||
Model Times Peak | Milliseconds | Milliseconds | ||
#1 Array, One Sprite | 44 | 64 | ||
#2 Array, Multiple Sprites | 30 | 48 | ||
#3 Scrollrect, Multiple Sprites | x | x | ||
#4 GAS, Multiple Sprites | x | x | ||
#5 Sprite Sheet, One Sprite | 49 | 56 | ||
5000 | 5000 | |||
Model Times Avg | Milliseconds | Milliseconds | ||
#1 Array, One Sprite | 40.971 | 43 | ||
#2 Array, Multiple Sprites | 27 | 26 | ||
#3 Scrollrect, Multiple Sprites | x | x | ||
#4 GAS, Multiple Sprites | x | x | ||
#5 Sprite Sheet, One Sprite | 43.442 | 44.18 | 84.66 | 87.435 |
5000 | 5000 | |||
Creation Time | Milliseconds | Milliseconds | ||
#1 Array, One Sprite | 151 | 525 | ||
#2 Array, Multiple Sprites | 601 | 1322 | ||
#3 Scrollrect, Multiple Sprites | x | x | ||
#4 GAS, Multiple Sprites | x | x | ||
#5 Sprite Sheet, One Sprite | 139 | 528 | ||
5000 | 5000 | |||
Memory Usage | Kilobytes | Kilobytes | ||
#1 Array, One Sprite | 8826 | 10833 | ||
#2 Array, Multiple Sprites | 12627 | 14573 | ||
#3 Scrollrect, Multiple Sprites | x | x | ||
#4 GAS, Multiple Sprites | x | x | ||
#5 Sprite Sheet, One Sprite | 8806 | 9973 |
10000 Objects
Model | METHOD | EVENT |
5000 / 1000 Objects | 10000 | 10000 |
Render (frame rate) Peak | FPS | FPS |
#1 Array, One Sprite | 12 | 12 |
#2 Array, Multiple Sprites | 9 | 9 |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 12 | 12 |
10000 | 10000 | |
Render (frame rate) Avg | FPS | FPS |
#1 Array, One Sprite | 12 | 11 |
#2 Array, Multiple Sprites | 8 | 8 |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 12 | 11 |
10000 | 10000 | |
Model Times Peak | Milliseconds | Milliseconds |
#1 Array, One Sprite | 88 | 107 |
#2 Array, Multiple Sprites | 64 | 85 |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 89 | 116 |
10000 | 10000 | |
Model Times Avg | Milliseconds | Milliseconds |
#1 Array, One Sprite | 82.572 | 86.555 |
#2 Array, Multiple Sprites | 59.82 | 61.61 |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 84.66 | 87.435 |
10000 | 10000 | |
Creation Time | Milliseconds | Milliseconds |
#1 Array, One Sprite | 273 | 1764 |
#2 Array, Multiple Sprites | 3847 | 7279 |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 290 | 1774 |
10000 | 10000 | |
Memory Usage | Kilobytes | Kilobytes |
#1 Array, One Sprite | 13062 | 17108 |
#2 Array, Multiple Sprites | 20627 | 23298 |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 13004 | 15085 |
Both the scrollrect and gas rendering engines (#3 and #4) ran too slow for me to get any good calculations. I left to take a shower and 30 minutes later the scrollrect version was still churning along. What is consistent is we cannot get an average frame rate above 23 for 5000 and 12 for10000, and #5 has finally started out outpace #2. The king of FPS is still #1 and the execution time gap between #2 and #1/#5 is closing. It seems that at 5000 objects, individual sprites are no longer a very good idea at all, especially give then 3+ seconds it takes just to initialize and create the objects. Memory usage is starting to become closer between #1 and #5 (arrays vs sprite sheets), and the Event model, while still slower, and more of a memory hog can sustain a pretty even frame rate (if slightly lower than the method call model).
15000 Object Test. This test started to show the seamy underbelly of my testing engine as the #2 tests could not finished because of exception errors in object cleanup and creation. The only number I could get was a max 6 FPS while watching the FPS counter.
Model | METHOD | EVENT |
15000 Objects | ||
Render (frame rate) Peak | FPS | FPS |
#1 Array, One Sprite | 8 | 8 |
#2 Array, Multiple Sprites | 6 | x |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 8 | 8 |
Render (frame rate) Avg | FPS | FPS |
#1 Array, One Sprite | 8 | 7 |
#2 Array, Multiple Sprites | x | x |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 8 | 7 |
Model Times Peak | milliseconds | milliseconds |
#1 Array, One Sprite | 130 | 141 |
#2 Array, Multiple Sprites | x | x |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 157 | 180 |
Model Times Avg | milliseconds | milliseconds |
#1 Array, One Sprite | 123 | 126.972 |
#2 Array, Multiple Sprites | x | x |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 126 | 129 |
Creation Time | milliseconds | milliseconds |
#1 Array, One Sprite | 402 | 3743 |
#2 Array, Multiple Sprites | x | x |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 426 | 3695 |
Memory Usage | Kilobytes | Kilobytes |
#1 Array, One Sprite | 17244 | 21594 |
#2 Array, Multiple Sprites | x | x |
#3 Scrollrect, Multiple Sprites | x | x |
#4 GAS, Multiple Sprites | x | x |
#5 Sprite Sheet, One Sprite | 17211 | 23592 |
So, at 15000 objects, the winner is #1, the current engine I am using for my game, by a slight margin. The sprite sheets are starting to consume more memory than the arrays. #1 is also a little faster and takes less time to complete object update executions. 8 Frames per second may not sound like much, but the GAS, individual sprite, and scroll rect versions would be in negative FPS and result in full system crashes if I even attempted them.
Summary
If you didn’t feel like reading all of the above (and who could blame you), here is a summary of the significant findings (at least to me).
The render Models will be referred to by number:
#1 Array of BitmapData, One Sprite for blitting to. |
#2 Array of BitmapData, one sprite for each object |
#3 scrollrect over sprite sheet, one sprite for each object |
#4 GAS, one sprite for each object |
#5 Sprite sheet using copyPixels rect, One Sprite for blitting to |
100 Onscreen Game Objects Moving and Animating
Fastest Average Frame Rate | 124: #1 Event Model/Method Models, #2 Event/Method Models, #3 with Method model, #5 with Method and Event Models. |
Slowest Average Frame Rate | 107: #4 Both Update Models |
Fastest Update Model | 0.0069 Milliseconds: #2 Method Model. |
Slowest Update Model | 0.978 Milliseconds: #1 Event Model |
Fastest Object Creation Time | 3 Milliseconds: #1 and #5 using both Update Models. |
Slowest Object Creation Time | 13 Milliseconds: #4 using both Update Models |
Least memory used | 5033 Kilobytes. #5 Method Model |
Most memory used | 5558 Kilobytes, #3 Event Model |
500 Onscreen Game Objects Moving and Animating
Fastest Average Frame Rate | 125: #1 Event Model/Method Models, #2 Method Model Method model, #5 Method Model.. |
Slowest Average Frame Rate | 18: #3 Event Model |
Fastest Update Model | 1 Millisecond: #4 Method Model. |
Slowest Update Model | 9.01 Milliseconds: #5 Event Model |
Fastest Object Creation Time | 9 Milliseconds: #1 Method Model. |
Slowest Object Creation Time | 92 Milliseconds: #4 Event Model |
Least memory used | 4661 Kilobytes. #5 Method Model |
Most memory used | 9207 Kilobytes, #3 Event Model |
1000 Onscreen Game Objects Moving and Animating
Fastest Average Frame Rate | 93: #1 Method Model |
Slowest Average Frame Rate | 8: #3 Both Update Models |
Fastest Update Model | 2.008 Milliseconds: #4 Method Model. |
Slowest Update Model | 4.585Milliseconds: #5 Event Model |
Fastest Object Creation Time | 29 Milliseconds: #1 Method Model, #5 Method Model |
Slowest Object Creation Time | 187 Milliseconds: #4 Event Model |
Least memory used | 5369 Kilobytes. #5 Method Model |
Most memory used | 13479 Kilobytes, #4 Method Model |
5000 Onscreen Game Objects Moving and Animating (#3 and #4 dropped because they were to slow to even run the test)
Fastest Average Frame Rate | 23: #1 Method Model |
Slowest Average Frame Rate | 17: #2 Both Update Models. |
Fastest Update Model | 26 Milliseconds: #2 Event Model. |
Slowest Update Model | 44.18 Milliseconds: #5 Event Model |
Fastest Object Creation Time | 139 Milliseconds: #5 Method Model |
Slowest Object Creation Time | 1322 Milliseconds: #2 Event Model |
Least memory used | 8806 Kilobytes. #5 Method Model (#1 Method was very close) |
Most memory used | 14573 Kilobytes, #2 Event Model |
10000 Onscreen Game Objects Moving and Animating ( #3 and #4 dropped because they were to slow to even run the test)
Fastest Average Frame Rate | 12: #1 & #5 Method Model |
Slowest Average Frame Rate | 8: #2 Both Update Models. |
Fastest Update Model | 59.82 Milliseconds: #2 Method Model. |
Slowest Update Model | 129 Milliseconds: #5 Event Model |
Fastest Object Creation Time | 273 Milliseconds: #1 Method Model |
Slowest Object Creation Time | 7279 Milliseconds: #2 Event Model |
Least memory used | 13004 Kilobytes. #5 Method Model (#1 Method was very close again) |
Most memory used | 23298 Kilobytes, #2 Event Model |
15000 Onscreen Game Objects Moving and Animating ( #2, #3 and #4 dropped because they were to slow to even run the test)
Fastest Average Frame Rate | 8: #1 & #5 Method Model |
Slowest Average Frame Rate | 7: #2 Both #1 and #5 Event Models |
Fastest Update Model | 123 Milliseconds: #1 Method Model. |
Slowest Update Model | 87.435 Milliseconds: #5 Event Model |
Fastest Object Creation Time | 402 Milliseconds: #1 Method Model |
Slowest Object Creation Time | 3743 Milliseconds: #1 Event Model |
Least memory used | 17211 Kilobytes. #5 Method Model (#1 Method was very close again) |
Most memory used | 23592 Kilobytes, #5 Event Model |
Ok, wow did that take a long time to write, run, and compile into useful (somewhat information). The whole process has taken me over a week to complete, but it was well worth it. I now have enough information to decide which model to use for which kind of application. I like the ease of Event driven update models, but as you can see above they are slightly slower than a method call model. It also looks to me like both the Array Copy Pixels and the sprite sheet Rect copy pixels (#1 and #5 respectively) are my best be for over 1000 objects. I will need to keep an eye out for their render update execution time if I am going to have a lot of other game logic going on.
Anyway, there you have it. You can download the .swf file here to run your own tests if you like. I don’t advise putting it in the browser if you are looking for ultimate numbers, but then again, it might be useful for testing the qualities of the different plug-in versions and browser variations…now I’ll save that for a post Christmas article.
On that note, below is a version you can play with. I don’t advise using over 1000 objects with the GAS or Scrollrect render models though.
I hope this helps you in some way, it sure was a learning experience for me. I am sure you will come across better, more optimized models and engines. If you find one that can do 15,000 objects are higher than 8FPS on a Dual Core Pentium D (3.2 GHZ / 3.2 GHZ) with Radion 7300 card (not the best card, I know) then shoot me an email. I’d love to test it out myself.
-Jeff
I got good ideas from this amazing blog.
Wonderful blog!