flashgameblog.at

Currently the posts are filtered by: code speed
Reset this filter to see all posts.

08.04.2010
10:56

Blitting - The art of fast Pixel drawing.

To display objects on screen in flash games Movieclips or Sprites are used very often. Sure this is the easiest way to handle it and if there are not much objects this is OK. But when performance comes in play there is a much better way to display objects - blitting. Blitting reduces the overhead of managing display objects in the flash player and allows you to use the saved CPU power to display more objects or to use it where you really need it.

Blitting


Blitting stands for BLock Image Transfer and describes a technique of drawing pixels on screen. The idea is quite simple. Instead of creating tons of MovieClips we create one Bitmap and paint all objects on it. So the flash player has just to manage one object. With flash 8 (I think) the BitmapData object was introduced. This object allows direct access of pixel data in images. And this is what we are going to do.

For this I'll write the same application one version with MovieClips and a second version with blitting. The application will be very simple. It will create moving objects on screen and as long as a certain FPS count isn't reached flash will add one object. If the FPS count falls to much one object will be removed automatically.

The average number of objects on screen will indicate which version runs at a better performance.

I'll not describe how to create MovieClips, but I'll explain how blitting is done.

First we need the BitmapData object. The BitmapData object represents the content of a Bitmap object. It gives us direct access on pixel basis. Additionally we need a Bitmap object. This object represents the display object we can add to stage, we also link our bitmapData object to it. This is the way our pixels comes on screen.

  1. ...
  2. import flash.display.BitmapData;
  3. import flash.display.Bitmap;
  4.  
  5. ...
  6. var screenData:BitmapData = new BitmapData(640,480,true,0x00000);
  7. var screen:Bitmap = new Bitmap(screenData);
  8. addChild(screen);
  9. ...

The BitmapData object offers different ways to manipulate its pixel content. The fastest was to bring something to it is the function copyPixels. With this functions we can transfer pixel data from one BitmapData object to another one. We also can determine the position where pixels will appear. For this we need an additional BitmapData object holding the data we want to show on screen. To do this we are simply drawing a MovieClip on it.

  1. ...
  2. import flash.display.MovieClip;
  3. ...
  4.  
  5. var mc:MovieClip = new Unit(); // Unit is the MovieClip in the Library
  6.  
  7. var objectData:BitmapData = new Bitmapdata(42,42,true,0x000000);
  8. objectData.draw(mc)// this draws mc to objectData
  9. ...

Technically this is all we need to do.

X and Y describes the point where objectData is drawn in screenData.

  1. ...
  2. screenData.copyPixels(objectData, new Recangle(0,0,42,42), new Point(x,y),null,null,true);
  3. ...

For more information about BitmapData object follow this link:

http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/BitmapData.html

 

demofile using MovieClips: click here

demofile using blitting: click here

Here you can download the source of the demo files: click here

28.12.2009
14:22

Preventing function calls brings performance

 

It isn't a secret. The overhead of function calls decreases the performance of your programs. So prevent them where ever you can. In software design encapsulation and modularity are two major, but oppositional topics. Good design finds the well balanced middle.

With some lines of code from my current project I want to show you how to prevent function calls at the usage of event dispatching. In my project I have one class dispatching an event to a lot of other objects. Each of these objects has got an ID and each event is just for one of them.

The easy way to code this is to register each object for the event and in the object's event handler I check the ID saved in the event object and skip the handler if the ID is not corresponding to the ID in the event object.

 

  1. ...
  2. for ( var i:int = 0; i < 200000; i++) {
  3.    arReceivers.push( new Receiever(i) );
  4.    sender.addEventListener( "test", arReceivers[i].evt );
  5. }
  6. ...
  7.  
  8. ...
  9. public function evt(e:MyEvent):void {
  10.     if (e.id != this.id )
  11.         return;
  12.  
  13.     // the event is for me
  14. }
  15. ..

In my test code I have 200,000 receiver objects and this codes leads two 200,000 function calls.

A better way of coding is to register one "global" handler for the event and to find the corresponding receiver object in this handler.

  1. ...
  2. sender.addEventListener( "test", evt );
  3.  
  4. for ( var i:int = 0; i < 200000; i++) {
  5.    arReceivers.push( new Receiever(i) );
  6. }
  7. ...
  8.  
  9. ...
  10. public function evt(e:MyEvent):void {
  11.     for ( var i:int = 0; i < arReceivers.length; i++) {
  12.         if (e.id == arReceivers[i].id ){
  13.             arReceivers[i].evt(e);
  14.             break;
  15.         }
  16.     }
  17. }
  18. ...
  19.  

In this code just two function calls occur independent from the number of receiver objects.

 

This is the last post for 2009. I wish you a happy new year and keep curious in 2010 :-)

17.11.2009
21:49

AST - ASToolBox (AS2)

 

This time I want to share a tool for all AS2 coders out there.

I wrote it because I needed an easy to use trace tool for my projects written with flashdevelop. At this time trace in flashdevelop wasn't an easy step and I didn't want to read all the tutorials because they simply didn't solve the problems for my installation.

ASToolBox (short ast) is a static class which offers functions for tracing, var_dump and time measurement of code segments.

Installation:
Copy the class source code below in a .as file and safe it in the folder

C:\Dokumente und Einstellungen\[USERNAME]\Lokale Einstellungen\Anwendungsdaten\Adobe\Flash CS4\de\Configuration\Classes\FP8


trace data
To trace data ast offers three functions. Firstly there is trace. It's the most common function in flash, so I think I don't need to explain it.

Secondly there we have var_dump. It takes an Object and traces an analysed string of it. It's an flash equivalent of the PHP function.

And Finally println, it works very similar as the C equivalent, but it doesn't know data types. Everything is handled as a String. Actually it doesn't output anything. It just prepares and returns a string for output.

Additionally to these output functions there is clear, which simply clears the output window.

time measurement

Sometimes you want to measure the time that is taken for a peace of code to be executed. Ast gives you some functions to make this task very simple.

 

  1.  
  2. ast.trace("In europe children get their presents on 24. December in the evening.");
  3. ast.var_dump({a:1, b:"w", c:["hello", "world"], d:{a:1, b:2, c:3}});
  4. ast.trace( ast.println("In europe %0 get their presents on %1. December in the evening%2", ["children", 24, "."]) );
  5. ast.clear();
  6.  
  7.  
  8. var z:Number = 0;
  9. var finish:Number = 0;
  10.  
  11. for( var i:Number = 0; i < 5; i++){
  12.   finish = 1000+Math.random()*10000;
  13.   ast.timer_start()
  14.   for( var a:Number = 0; a < finish; a++)
  15.     z = --z +1;
  16.   ast.timer_stop();
  17. }
  18.  
  19. ast.timer_report();
  20. ast.timer_avg();
  21.  
  22. // if you don't call clear here the measurements
  23. // above are added to the report below.
  24. ast.timer_clear();
  25.  
  26. var z:Number = 0;
  27. var finish:Number = 0;
  28.  
  29. for( var i:Number = 0; i < 5; i++){
  30.   finish = 1000+Math.random()*10000;
  31.   var handle:Number = ast.timer_start()
  32.   for( var a:Number = 0; a < finish; a++)
  33.     z = --z +1;
  34.   ast.timer_stop(handle2);
  35. }
  36.  
  37. ast.timer_report();
  38. ast.timer_avg();
  39.  

 

Here you can download ast.as

 

11.11.2009
01:29

If-Statement beschleunigen 2 b

Im ersten Teil dieses Posts haben wir uns eine Umgebung gebaut in der wir

 

  1. messen können, ob unser Code schneller läuft und
  2. ob der Code nach der Optimierung immer noch das gleiche tut.

 

Wir haben also unser Labor aufgebaut und nun können wir mit der Arbeit beginnen.

Hier noch mal der originale Code:

 

  1. if(
  2.   (! (diodeMidLeft & 1) && ( !climbing || !ladderMidRight && climbing ) )
  3.   ||
  4.   (! (diodeTopLeft & 1) && ( !climbing || !ladderTopLeft  && climbing ) )
  5. )
  6.   return true;
  7. else
  8.   return false;
  9. }
  10.  

 

Oder-Verknüpfung aufgelöst:

 

  1. if(! (diodeMidLeft & 1) && ( !climbing || !ladderMidRight && climbing ) )
  2.   return true;
  3.  
  4. if(! (diodeTopLeft & 1) && ( !climbing || !ladderTopLeft  && climbing ) )
  5.   return true;
  6.  
  7. return false;
  8.  

 

Als zweiten Schritt können wir auch die UND-Verknüpfung auflösen:

 

  1. if(! (diodeMidLeft & 1) )
  2.   if( !climbing || !ladderMidRight && climbing )
  3.     return true;
  4.  
  5. if(! (diodeTopLeft & 1) )
  6.   if( !climbing || !ladderTopLeft  && climbing )
  7.     return true;
  8.  
  9. return false;
  10.  

 

Nun haben wir wieder eine Oder-Verknüpfung:

 

  1. if(! (diodeMidLeft & 1) ){
  2.   if( !climbing )
  3.     return true;
  4.   if( !ladderMidRight && climbing )
  5.     return true;
  6. }
  7.    
  8. if(! (diodeTopLeft & 1) ){
  9.   if( !climbing)
  10.     return true;
  11.   if( !ladderTopLeft  && climbing )
  12.     return true;
  13.  
  14. }
  15.  
  16. return false;
  17.  

 

Und schließlich noch mal eine Und-Verknüpfung:

 

  1. if(! (diodeMidLeft & 1) ){
  2.   if( !climbing )
  3.     return true;
  4.   if( !ladderMidRight)
  5.     if( climbing )
  6.       return true;
  7. }
  8.    
  9. if(! (diodeTopLeft & 1) ){
  10.   if( !climbing)
  11.     return true;
  12.   if( !ladderTopLeft)
  13.     if( climbing )
  14.       return true;
  15.  
  16. }
  17.  
  18. return false;
  19.  

 

Das war's.

 

Wie und wann kann man Auflösen?

 

ODER

 

  1. if(A || B || C)
  2.   foo();
  3.  
  4. // ist das selbe wie
  5. if(A)
  6.   foo();
  7. else if(B)
  8.   foo();
  9. else if(C)
  10.   foo;
  11.  

 

"Else if" ist hier notwendig um eine mehrfache Ausführung von foo zu verhindern. Würde statt foo die Funktion mit return verlassen werden, könnte man einfache If-Anweisungen unter einander schreiben, da der Code nach return ohnedies nicht mehr ausgeführt werden würde

 

  1. if(A || B || C)
  2.   return true;
  3.  
  4. // ist das selbe wie
  5. if(A)
  6.   return true;
  7. if(B)
  8.   return true;
  9. if(C)
  10.   return true;
  11.  

 

UND

 

  1. if(A && B && C)
  2.   foo();
  3.  
  4. // ist das selbe wie
  5. if(A)
  6.   if(B)
  7.     if(C)
  8.       foo();
  9.  

 

Problematisch sind If-Anweisungen, die eine Else-Klause besitzen und die "nur" Code ausführen ohne den Code mit return verlassen. Hier sind nur sehr selten Auflösungen möglich.

 

Fazit:

 

Ich habe hier grundlegend die Möglichkeit der Optimierug ausgezeigt. Wir haben jedoch auch gesehen, dass der, dazu nötige, Aufwand nicht zu unterschätzen ist und die Beschleunigung unter Umständen nicht sehr groß ist. Bei mir lief der optimierte Code um 7 Millisekunden schneller (Bitte nicht übersehen, dass er 1.000.000 mal ausgeführt wird). Deshalb sollte man sich genau überlegen ob und an welchen Funktionen im eigenen Code man Hand anlegen sollte. Natürlich kommen die Funktionen, die am häufigsten ausgeführt werden am ehesten in Frage.

 

Ich habe im ersten Teil erwähnt, dass ich den Beispielcode gekürzt habe, da wir sonst über 2.000 Prüfungsfälle gehabt hätten. Ein Array mit 2000 Prüfungsfällen schreibt man natürlich nich von Hand. Hier schreibt man sich ein kurzes Programm, das das gewünschte Array ausgibt. Wenn man dieses Programm nicht schreiben kann oder will, kann man auch einen "brute-force" Angriff auf die Funktion starten. Hierbei erzeugt man die einzelnen Parameter zufällig und lässt den Test so lange laufen, bis wahrscheinlich alle Testfälle ausgeführt wurden. Ich halte die zweite Möglichkeit für eher unprofessionell und rate davon ab!

04.11.2009
23:48

If-Statement beschleunigen 2 a

Die Optimierung im vorgehenden Beispiel war so einfach, dass man Optimierung ohne weiteres durchführen kann. Wenn die Struktur der Bedingung jedoch komplizierter gestaltet ist, muss man Vorkehrungen treffen um sicherzustellen, dass die Logik des Statements nicht geändert. Das kann zu Fehlern führen, die sich erst später bzw. an anderer Stelle im Quellcode bemerkbar machen. Für diesen Fehler die Ursache zu finden ist mühsam und zeitaufwendig! Darüber hinaus möchte man natürlich auch wissen, ob die Anstrengungen der Optimierung überhaupt Früchte trägt. Also brauchen wir einen Mechanismus zum Messen der Ausführungszeit.

 

Zeitmessung

 

Die grundlegende Funktion zur Zeitmessung ist die Funktion getTimer(). Sie liefert die vergangen Millisekunden seit dem 1. Jannuar 1970(Weiß eigentlich jemand warum genau dieses Datum? Mein Informatikprofessor konnte mir diese Frage damals nicht beantworten). Der Quellcode zu einer Messung könnte folgender Maßen aussehen:

 

  1. var _start:int = getTimer();
  2.  
  3. // ...
  4. // zu messender code
  5. // ...
  6.  
  7. var _finish:int = getTimer();
  8. trace("ausführungsdauer:",(_finish-_start));

 

Funktionsprüfung

 

Hier wieder ein If-Statement aus meinem aktuellen Projekt(Ist die nicht wunderschön= ;-) Na, wenn da kein Potential zur Optimierung drin steckt. Ich weiß es selbst noch nicht.):

 

  1. function test():Boolean{
  2.   if(
  3.     (! (diodeMidLeft & 1) && ( !climbing || !ladderMidRight && climbing ) )
  4.     ||
  5.     (! (diodeTopLeft & 1) && ( !climbing || !ladderTopLeft  && climbing ) )
  6.   )
  7.     return true;
  8.  
  9.   else
  10.     return false;
  11. }

 

Um sicher zu stellen, dass wir die Funktion nicht verändern, müssen wir zunächst einmal analysieren, was da alles abgefragt wird. Anschließend müssen wir uns eine Tabelle erstellen, in der wir für jede mögliche Kombination der einzelnen Bedingungen das Funktionsergebnis speichern. Und nach der Optimierung müssen wir prüfen, ob die Ergebnisse der neuen Funktion mit den Ergebnissen alten ident sind. Analyse: diode ist eine klasse mit statischen Konstanten. climbing ist ein boolean Wert. _tiles ist ein Array mit Objekten, wobei uns hier nur die Members diode(int) und ladder(Boolean) interessieren _level ist ein zweidimensionales Array, mit Zahlen, die auf _tiles verweisen.

 

Zunächst werden wir die Struktur etwas vereinfachen:

 

  1. Konstanten werden mit deren Wert ersetzt.
  2. Die Arrays werden durch einfache Variablen ersetzt.

 

 

  1. function test():Boolean{
  2.   if(
  3.     (! (diodeMidLeft & 1) && ( !climbing || !ladderMidRight && climbing ) )
  4.     ||
  5.     (! (diodeTopLeft & 1) && ( !climbing || !ladderTopLeft  && climbing ) )
  6.   )
  7.     return true;
  8.  
  9.   else
  10.     return false;
  11. }

 

Nun müssen wir festlegen welche Werte zum Prüfen verwenden müssen, um alle Fälle abzudecken.

Ich verzichte hier darauf die Bit-Operatoren erklären, da dies den Blogeintrag sprengen würde.

 

Für die Variablen diode... genügt es die werte 1 und 2 zu prüfen.
climbing wird mit true und false geprüft.
ladder... sind auch Variablen vom Typ Boolean und werden somit auch mit true und false geprüft.

 

Wir haben nun fünf Variablen, die jeweils zwei Werte annehmen können. Daraus ergeben sich 32 mögliche Kombinationen.

Ich muss hier fairer Weise eingestehen, dass ich die Funktion gekürzt habe. In meinem Framework werden hier elf Variablen verwendet, was über 2000 Prüfungsfälle ergibt. Ich komme da später darauf zurück.

 

Daraus ergibt sich folgende Tabelle:

 

diodeMidLeft,diodeTopLeft,ladderMidRight,ladderTopLeft,climbing 0,0,false,false,false
0,0,false,false,true
0,0,false,true,false
0,0,false,true,true
0,0,true,false,false
0,0,true,false,true
0,0,true,true,false
0,0,true,true,true
0,1,false,false,false
0,1,false,false,true
0,1,false,true,false
0,1,false,true,true
0,1,true,false,false
0,1,true,false,true
0,1,true,true,false
0,1,true,true,true
1,0,false,false,false
1,0,false,false,true
1,0,false,true,false
1,0,false,true,true
1,0,true,false,false
1,0,true,false,true
1,0,true,true,false
1,0,true,true,true
1,1,false,false,false
1,1,false,false,true
1,1,false,true,false
1,1,false,true,true
1,1,true,false,false
1,1,true,false,true
1,1,true,true,false
1,1,true,true,true

 

Jetzt haben wir alles, was wir brauchen um mit der eigentlichen Arbeit zu beginnen. Wir brauchen nur noch ein wenig Code:

 

  1. Wir packen die Prüfwerte in ein Array um sie besser verarbeiten zu können.
  2. Wir kopieren die Funktion A und benennen sie B. Diese wird dann optimiert.
  3. In einer Schleife rufen wir beide Funktionen auf und vergleichen die Ergebnisse
  4. Wir messen die Zeit, um zu sehen, ob die Optimierung erfolgreich war.

 

 

  1. var werte:Array = [
  2.   [0,0,false,false,false],
  3.   [0,0,false,false,true],
  4.   [0,0,false,true,false],
  5.   [0,0,false,true,true],
  6.   [0,0,true,false,false],
  7.   [0,0,true,false,true],
  8.   [0,0,true,true,false],
  9.   [0,0,true,true,true],
  10.   [0,1,false,false,false],
  11.   [0,1,false,false,true],
  12.   [0,1,false,true,false],
  13.   [0,1,false,true,true],
  14.   [0,1,true,false,false],
  15.   [0,1,true,false,true],
  16.   [0,1,true,true,false],
  17.   [0,1,true,true,true],
  18.   [1,0,false,false,false],
  19.   [1,0,false,false,true],
  20.   [1,0,false,true,false],
  21.   [1,0,false,true,true],
  22.   [1,0,true,false,false],
  23.   [1,0,true,false,true],
  24.   [1,0,true,true,false],
  25.   [1,0,true,true,true],
  26.   [1,1,false,false,false],
  27.   [1,1,false,false,true],
  28.   [1,1,false,true,false],
  29.   [1,1,false,true,true],
  30.   [1,1,true,false,false],
  31.   [1,1,true,false,true],
  32.   [1,1,true,true,false],
  33.   [1,1,true,true,true]
  34. ];
  35.  
  36. function A(diodeMidLeft:int,diodeTopLeft:int,ladderMidRight:Boolean,ladderTopLeft:Boolean,climbing:Boolean):Boolean{
  37.   if(
  38.     (! (diodeMidLeft & 1) && ( !climbing || !ladderMidRight && climbing ) )
  39.     ||
  40.     (! (diodeTopLeft & 1) && ( !climbing || !ladderTopLeft  && climbing ) )
  41.   )
  42.     return true;
  43.  
  44.   else
  45.     return false;
  46. }
  47.  
  48. function B(diodeMidLeft:int,diodeTopLeft:int,ladderMidRight:Boolean,ladderTopLeft:Boolean,climbing:Boolean):Boolean{
  49.   if(
  50.     (! (diodeMidLeft & 1) && ( !climbing || !ladderMidRight && climbing ) )
  51.     ||
  52.     (! (diodeTopLeft & 1) && ( !climbing || !ladderTopLeft  && climbing ) )
  53.   )
  54.     return true;
  55.   else
  56.     return false;
  57. }
  58.  
  59. for( var i:int = 0; i< werte.length;i++)
  60.   trace(  "case:",
  61.     i,
  62.     A(werte[i][0],werte[i][1],werte[i][2],werte[i][3],werte[i][4]) == B(werte[i][0],werte[i][1],werte[i][2],werte[i][3],werte[i][4])
  63.   );
  64.  
  65. var _start:int = getTimer();
  66. i = 0;
  67.  
  68.  
  69. while((i+=5)<1000000){
  70.   A(werte[i%5][0],werte[(i+1)%5][1],werte[(i+2)%5][2],werte[(i+3)%5][3],werte[(i+4)%5][4] );
  71. }
  72.  
  73. var _finish:int = getTimer();
  74. trace("ausführungsdauer Funktion A:",(_finish-_start));
  75.  
  76. _start = getTimer();
  77. i = 0;
  78.  
  79. while((i+=5)<1000000){
  80.   B(werte[i%5][0],werte[(i+1)%5][1],werte[(i+2)%5][2],werte[(i+3)%5][3],werte[(i+4)%5][4] );
  81. }
  82.  
  83. _finish = getTimer();
  84. trace("Ausführungsdauer Funktion B:",(_finish-_start));

 

Ich werde die eigentliche Optimierung im nächten Post zeigen, um die Länge dieses Eintrages nicht überzustrapazieren.

< < September 2010 > >
S M T W T F S
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    

Blog rolls

Latest Comments

ISO Blender Camera Setup
24.04.2010 06:58
update
21.03.2010 01:15
if-Statement
16.11.2009 08:34
hmm...
16.11.2009 08:00

Archive

  • [-]2010(8)
    • [-]August(1)
    • [-]July(1)
    • [-]June(2)
    • [-]April(1)
    • [-]March(2)
    • [-]February(1)
  • [-]2009(13)
    • [-]December(4)
    • [-]November(8)
    • [-]October(1)

Copy and paste this link into your RSS news reader

RSS 0.91Posts
RSS 2.0Posts

Social Bookmarking

Bookmark bei: Mr. Wong Bookmark bei: Webnews Bookmark bei: Icio Bookmark bei: Oneview Bookmark bei: Linkarena Bookmark bei: Favoriten Bookmark bei: Seekxl Bookmark bei: Favit Bookmark bei: Social Bookmarking Tool Bookmark bei: Power Oldie Bookmark bei: Bookmarks.cc Bookmark bei: Newskick Bookmark bei: Newsider Bookmark bei: Linksilo Bookmark bei: Readster Bookmark bei: Folkd Bookmark bei: Yigg Bookmark bei: Digg Bookmark bei: Del.icio.us Bookmark bei: Reddit Bookmark bei: Simpy Bookmark bei: StumbleUpon Bookmark bei: Slashdot Bookmark bei: Netscape Bookmark bei: Furl Bookmark bei: Yahoo Bookmark bei: Spurl Bookmark bei: Google Bookmark bei: Blinklist Bookmark bei: Blogmarks Bookmark bei: Diigo Bookmark bei: Technorati Bookmark bei: Newsvine Bookmark bei: Blinkbits Bookmark bei: Ma.Gnolia Bookmark bei: Smarking Bookmark bei: Netvouz Information