Pastor Pixel Tipps Tutorial Anleitungen Hilfe

Starling

Das Framework Starling ermöglicht eine höhere Performance, da mittels der Adobe Molehill Technologie über open GL direkt auf die Grafikkarte zugegriffen wird. Diese sogenannte Stage3D Architektur von Adobe wurde in Flash / Air integriert. Der Inhalt wird komplett von der GPU gerenderd. Beispielsweise 3D Szenarien oder komplexe Animationen mittels Perlin Noise oder ähnlichem sollte man mit dieser neuen Technik erstellen. (Siehe hierzu auch meinen Tipp Bitmap Data, viele der dort aufgeführten Beispiele kann man sich leicht umschreiben, um eine bessere Performance zu erhalten.

AufgepasstDiese GPU Oberfläche liegt unter der Display List. Das bedeutet in der Praxis das alles, was mit der Display List gemacht wird, nicht über den Stage3D Content gelegt werden kann. Oder salopp ausgedrückt: Es ist also nicht möglich Dinge mit Starling zu erstellen und Dinge, die händisch oder auch programmatisch auf herkömmliche Art erzeugt wurden, zusätzlich anzuzeigen.

Die Starling Api ist der AS3 Api sehr ähnlich. Es gibt jedoch einige Unterschiede, die es zu erlernen gilt.

Nähere Infos auf der offiziellen Starling Seite.

Dort finden Sie auch einen Link zum Download des Frameworks.

Ein besonders guter Einstieg ist das kostenlose Buch "Introducing Starling" geschrieben von Thibault Imbert im O'Reilly Shop

Die Starling Api Reference
Download des Starling Frameworks

Flash Builder 4.5 für Starling einrichten

Viele dieser Beispiele habe ich aus dem oben genannten Buch "Introducing Starling" übernommen und etwas abgeändert.

Ich erkläre die ersten Beispiele mit der Flash IDE. Doch wenn es darum geht Starling MovieClips zu erstellen, gehe ich langsam dazu über das mit Flash Builder zu machen. Sofern man Flash Builder hat, ist das sowieso die bessere Wahl, da man die Funktionen der Flash IDE mit Starling Projekten eigentlich nicht nutzen kann. Außerdem gibt es noch die kostenlose Alternative "FlashDevelop". Spenden Sie etwas an die FD Community, wenn Sie Flash Develop benutzen.

Sollten Sie Flash Builder 4.6 besitzen ist alles im grünen Bereich. Sollten Sie Flash Builder 4.5 haben, müssen Sie dieses folgendermaßen einrichten.

Video Tutorial -flash-builder-4-5 für Starling bzw. Stage 3D einrichten

Im folgenden fasse ich die Schritte zusammen, die in dem Videotutorial erläutert werden:

Laden Sie sich die neueste Version der Flex SDK bei Adobe runter und entpacken Sie diese. Vorzugsweise in einen dafür vorgesehen Ordner. Programme Adobe / Adobe Flash Builder / sdks / und erstellen Sie sich dort einen passenden Unterordner.

Laden Sie sich das Starling Framework runter. hier die offizielle Starling Seite.

Flash Builder öffnen, Fenster / Voreinstellungen

Geben sie oben unter Filter "sdk" ein, dann erscheint, "Installierte Flex SDKs"

Füge eine neue hinzu und verweise auf den Ordner, welcher die aktuelle entpackte flex sdk beinhaltet.

Neu, Neues Actionscript Projekt

Name vergeben und unter "spezielle SDK Version" die neue hinzufügen bzw. auswählen. Weiter

Quellpfad, Starling Framework auswählen

Die Actionscriptdateien entnehmen Sie bitte dem nächsten Beispiel.

Sollte es nicht funktionieren schauen Sie nochmal in den "Einstellungen des Projekts, ob unter Compiler auch die richtige Flex SDK gewählt ist und auch ob das Starling Framework richtig eingebunden ist.

Desweiteren muss in der HTML Datei der wmode den Wert direct bekommen. Öffnen Sie im Projektunterordner HTML Template, die html Datei und fügen Sie an 3 Stellen den parameter wmode value = direct ein. Achten Sie auf die genaue Schreibweise der anderen Parameter, am besten Sie kopieren sich jeweils eine Parameterzeile und ersetzen die entsprechenden Stellen.

Flash Player Debugger Version

Hat man nicht die aktuelle Flashplayer Version installiert, kann man diese im folgenden Ordner ersetzen:

C:/Programme/ Adobe / Adobe Flash Builder 4.5 / player / win

First Step Flash IDE

Speichere das Framework und binde es ein. Siehe dazu meinen Tipp Klassenbibliotheken.

In fast allen Starling Tipps wird mit Flex und FlashBuilder gearbeitet. Ich erkläre alles mit der Flash IDE. Die Unterschiede sind nicht der Rede wert.

Aufbau der Szene

Erzeuge eine Klasse als Dokumentenklasse genannt Startup:

 

package 
{
	import flash.display.Sprite;
	import starling.core.Starling;

	public class Startup extends Sprite
	{
		private var _starling:Starling;


		/*public function Starling(rootClass:Class, stage:flash.display.Stage,
		viewPort:Rectangle=null, stage3D:Stage3D=null,
		renderMode:String="auto")*/

		public function Startup()
		{
			_starling = new Starling(Game,stage);
			_starling.start();
		}

	}

}

Der Klassenname muss im Eigenschaftenfenster der Fla Datei, welche im gleichen Ordner abgespeichert wird, unter Klasse eingegeben werden.

Aufbau eines einfachen Beispiels

Erzeugen wir nun eine Klasse namens Game, welche in der Starling Klasse als erstes Argument übergeben wurde:

package 
{
	import starling.display.Sprite;
	import starling.text.TextField;
	import starling.textures.Texture;
	import starling.display.Image;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import starling.utils.deg2rad;
	



	public class Game extends Sprite
	{
		public function Game()
		{
			var textField:TextField = new TextField(400,100,"Welcome to Starling!");
			textField.text = "Halleluja";
			addChild(textField);

			// create a Texture object out of an embedded bitmap
			
			var texture:Texture = Texture.fromBitmapData ( new Jesus());
			// create an Image object our of the Texture
			var image:Image = new Image(texture);
			// set the properties
			image.pivotX = 0;
			image.pivotY = 0;
			image.x = 320;
			image.y = 80;
			image.rotation = starling.utils.deg2rad(10);
			// display it
			addChild(image);


		}


	}

}

Hier wird ein Textfeld und Bild auf die Bühne gebracht. Vorraussetzung für das Bild ist ein in die Bibliothek importiertes Bild mit Export für Actionscript Klassenname "Jesus" erbende Klasse: flash.display.BitmapData. Siehe dazu meinen Tipp Bitmap

Aufgepasst Unter Einstellungen für Veröffentlichungen HTML muss als Fenstermodus die Option "direct" eingegeben werden.

Aufgepasst Mittels pivotX und pivotY lässt sich der Registrierpunkt neu bestimmen

Aufgepasst Die Eigenschaft rotation erwartet einen Wert im Bogenmaß. Für die Umrechnung stehen in Starling 2 praktische Methoden zur Verfügung deg2rad() und rad2deg() Degree zu Radians und umgekehrt.

 

TouchEvent anstatt MouseEvents

In Starling gibt es keine Mouse Events stattdessen benutzt man Touch Events. Ein Touch Event tritt ein, wenn die Maus oder der Finger (Mobile) das Objekt berührt. Da es verschiedene EventPhasen gibt, gibt es hier viele Möglichkeiten

 

private function onTouchedSprite(e:TouchEvent):void
{
// get the touch points (can be multiple because of multitouch)
var touch:Touch = e.getTouch(stage);
var clicked:DisplayObject = e.currentTarget as DisplayObject;
// detect the click/release phase
if ( touch.phase == TouchPhase.ENDED )
{
// remove the clicked object
removeChild(clicked, true);
}
}

Der Touch Event wird auf eine Phase hin abgefragt und entsprechend darauf reagiert. Es gibt weitere Phasen wie

hover (MouseOver)
began (MouseDown)
moved (mit gedrückter Maustaste ziehen
stationary (bleibt über dem Objekt keine Mausaktivität)
ended (Clicked)

Im folgenden Beispiel sieht man einige Möglichkeiten. Man bekommt die Mausposition auf der Bühne und speichert diese in einer Point Instanz. Außerdem wird die touch.phase ermittelt.

		private function onTouch(e:TouchEvent):void
		{
			// Mouse Position in Bezug auf die stage
			var touch:Touch = e.getTouch(stage);
			var pos:Point = touch.getLocation(stage);
			trace( touch.phase );
		}

Über den Event Parameter eines Touch Events kann man auch andere Dinge abfragen, zum Beispiel:
trace(e.ctrlKey);
liefert true, sobald die Strg Taste gedrückt wird. Weitere Parameter sind:

getTouch
das erste TouchObjekt über einem bestimmten Ziel in einer bestimmten Phase

getTouches
Eine Anzahl von Touch Objekten über einem bestimmten Ziel in einer bestimmten Phase

ctrlKey
Liefert einen Boolschen Wert ob Strg Taste gedrückt wurde oder nicht.

shiftkey
Liefert einen Boolschen Wert ob Strg Taste gedrückt wurde oder nicht.

timestamp
Die Zeit in Sekunden als das Ereignis eintrat seit die Anwendung began.

touches
Alle touches, die zur Zeit stattfinden. Im folgenden Codeschnipsel wird ein Vector erzeugt, in dem alle touches enthalten sind.
var touches:Vector.<Touch > = e.touches;

Die Methoden und Eigenschaften eines Touch Objekts

Im vorigen Code Beispiel wird ein Touch Objekt erzeugt.
var touch:Touch = e.getTouch(stage);
Folgende Methoden und Eigenschaften gibt es:

clone
Ein Objekt wird gecloned.

getLocation
Die Position eines Touch wird in das lokale Koordinatensystem eines Display Objekts übertragen.


getPreviousLocation
Die Position des vorigen Touch wird in das lokale Koordinatensystem eines Display Objekts übertragen.


globalX
Die xPositon des Touch in Screen Koordinaten


globalY
Die yPositon des Touch in Screen Koordinaten


id
Eine eindeutige id für das Objekt


phase
Die aktuelle Phase, in der sich Objekt befindet


previousGlobalX

Die vorige xPositon des Touch in Screen Koordinaten


previousGlobalY
Die vorige yPositon des Touch in Screen Koordinaten


tapCount
The number of taps the finger made in a short amount of time. Use this to detect double-taps, for example.


target
The display object at which the touch occurred.


timestamp
Die Zeit in Sekunden als das Ereignis eintrat seit die Anwendung began.

MultiTouch Events

Multi touch Events kann auf dem Desktop schlecht ausprobieren. Starling bietet jedoch eine Möglichkeit auch auf dem Desktop Multitouch Events zu simulieren.

mStarling.simulateMultitouch = true;

Dazu muss die simulateMultitouch Eigenschaft der Starling Instanz auf true gesetzt werden. Wenn man dann auf dem Desktop die Strg-Taste drückt erscheinen 2 Punkte, stellvertretend für die beiden Touchpunkte.

Im folgenden Beispiel wird der Abstand zwischen 2 Touches berechnet:

		private function onTouchedSprite(e:TouchEvent):void
		{
			// retrieves the touch points
			var touches:Vector.  = e.touches;
			// if two fingers
			if (touches.length == 2)
			{
				var finger1:Touch = touches[0];
				var finger2:Touch = touches[1];
				var distance:int;
				var dx:int;
				var dy:int;
				// if both fingers moving (dragging)
				if (finger1.phase == TouchPhase.MOVED && finger2.phase == TouchPhase.MOVED)
				{
					// calculate the distance between each axes
					dx = Math.abs(finger1.globalX - finger2.globalX);
					dy = Math.abs(finger1.globalY - finger2.globalY);
					// calculate the distance
					distance = Math.sqrt(((dx * dx) + dy * dy));
					trace(distance);
				}
			}
		}

 

 

Event Listener löschen

Beachte im vorigen Code Beispiel auch den zweiten Parameter bei removeChild(child, dispose) , der hier auf true gesetzt wurde, er bewirkt das alle EventListener des Objekts gelöscht wurden.

Man kann auch alle Listener entfernen, indem man dispose explicit auf einem Display Object aufruft:

myObj.dispose();

Außerdem gibt es in Starling neben dem removeEventListener auch removeEventListeners, welches alle EventListener eines bestimmten Typs entfernt oder ohne Argument alle EventListener entfernt..

button.removeEventListeners(Event.TRIGGERED);

button.removeEventListeners();

Man kann abfragen ob, ein bestimmter Event registriert wurde

myObj.hasEventListener(e.type)

 

Texture

Ein Texutre Object muss erstellt werden um ein Image Objekt zu füttern. Man kann es sich ähnlich vorstellen, wie die Beziehung zwischen BitmapData und Bitmap.

Wenn man ein Texture Object erzeugt, braucht man die Texture API. Ich zitiere aus dem Buch "Introducing Starling" by Thibault Imbert, Verlag O`Reilly

base
The Stage3D texture object the texture is based on.

dispose
Disposes the underlying texture data.

empty
Returns a Texture object out of dimensions (width and height).

frame
The texture frame (see class description).

fromBitmap
Returns a Texture object out of a Bitmap object. This Bitmap object can be embedded or loaded dynamically.

fromBitmapData
Returns a Texture object out of a BitmapData object.

fromAtfData
Allows the use of a compressed texture using the ATF (Adobe Texture Format). Compressed textures allows you to save a lot of memory especially on constrained environments like mobile devices.

fromTexture
Allows the use of a texture and returns a new texture.

height
The height of the texture in pixels.

mipmapping
Indicates if the texture contains mip maps.

premultipliedAlpha
Indicates if the alpha values are premultiplied into the RGB values.

repeat
Indicates if the texture should repeat like a wallpaper or stretch the outermost pixels.

width
The width of the texture in pixels. Different image formats can be used for your textures. The following list summarizes the various formats that can be used for your textures:

PNG
As alpha channel is often required, PNG is one of the most common file format used for textures.

JPEG
The classic JPEG format can also be used. Remember that on the GPU the image will be decompressed, so using JPEG will not limit the memory usage and you will not be able to use transparency in your textures.

ATF
Adobe Texture Format. This is the best file format for the best compression. ATF files are primarily a file container to store lossy texture data. It achieves its lossy compression through to the use of two common techniques: JPEG-XR1 compression and block based compression. JPEG-XR compression provide a competitive method to save storage space and network bandwidth. Block based compression provides a way to reduce texture memory usage on the client, at a fixed ratio of 1:8 compared to RGBA textures. ATF supports three types of block based compression: DXT1/5, ETC1/3 and PVRTC4.

Image

In Starling, a starling.display.Image object is the equivalent of a native flash.display.Bitmap object.

Im folgenden Beispiel werden Bilder zur Anzeige gebracht. In meinem Beispiel für die Flash IDE habe ich in die Bibliothek der fla Datei ein Bild importiert und diesem mittels "Export für Actionscipt" den Klassennamen "SausageData" gegeben. Näheres dazu in meinem Tipp Bitmap.

Weitere Hinweise siehe Kommentare im Quelltext.

Achtung, diese Klasse für sich alleine funktioniert nicht, sie muss, wie im ersten Beispiel in der Starling Klasse aufgerufen werden.

package 
{
	import flash.display.Bitmap;
	import starling.display.Image;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	import starling.utils.deg2rad;
	public class Game extends Sprite
	{
		private var sausagesVector:Vector.  = new Vector. (NUM_SAUSAGES,true);
		private const NUM_SAUSAGES:uint = 400;


		public function Game()
		{
			addEventListener(Event.ADDED_TO_STAGE,onAdded);
		}
		private function onAdded(e:Event):void
		{
			// erzeuge ein Texture object um das  Image object zu füttern
			//SausageData ist der Klassenname eines BitmapData Objektes. Es ist ein importiertes Bild
			//in der Bibliothek der fla mitExport für Actionscript und Klassenename SausageData
			var texture:Texture = Texture.fromBitmapData(new SausageData());
			for (var i:int = 0; i < NUM_SAUSAGES; i++)
			{
				// erstelle ein Image object mit dieser einen texture
				var image:Image = new Image(texture);
				// Zufallswerte für  alpha, position, rotation
				image.alpha = Math.random();
				// Startposition und Drehung zuweisen
				image.x = Math.random() * stage.stageWidth;
				image.y = Math.random() * stage.stageHeight;
				image.rotation = deg2rad(Math.random() * 360);
				// show it
				addChild(image);
				// Referencen auf die einzelnen images werden im Vector sausageVector gespeichert.
				sausagesVector[i] = image;
			}
		}
	}
}

Die Image Klasse erbt die Methoden und Eigenschaften der DisplayObject Klasse. Außerdem gibt es eine spezielle smoothing Eigenschaft:

image.smoothing = TextureSmoothing.NONE;

Folgende Werte gibt es:
NONE
BILINEAR
TRILINEAR

Bilder laden

Wenn man ein Image Objekt erstellen will, mit einem von außen geladenem Bild, so kann man das mit den üblichen Loader, URLRequest sowie den dazugehörigen Events machen. Hierbei muss man jedoch darauf achten, dass die Events richtig zugewiesen werden, denn man braucht 2 Import Anweisungen

import starling.events.Event;
import flash.events.Event;

In der Funktion wird dem Argument die Eventklasse genau zugewiesen:

protected function onComplete(evt:flash.events.Event):void.....etc.

Auch im EventListener wird die genaue Zuweisung benötigt:

loader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE, onComplete );

Gleiches gilt dann auch für alle Events, die auf die starling.events verweisen.

myfunction (evt: starling.events.Event)
und im Listener
addEventListener(starling.events.ADDED_TO_STAGE, onAdded)

Hier nun das komplette Beispiel:

Achtung, diese Klasse für sich alleine funktioniert nicht, sie muss, wie im ersten Beispiel in der Starling Klasse aufgerufen werden.

package 
{
	import flash.display.Bitmap;
	import starling.display.Image;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	import starling.utils.deg2rad;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.net.URLRequest;


	public class Game extends Sprite
	{

		private var loader:Loader;
		private var texture:Texture;


		public function Game()
		{

			//achte auf den direkten Verweise starling.events.Event.....
			//denn auch die flash.events. wurden importiert.
			addEventListener(starling.events.Event.ADDED_TO_STAGE,onAdded);

		}
		//achte auf den Verweise  e:starling.events.Event
		private function onAdded(e:starling.events.Event):void
		{

			loader = new Loader();
			loader.load( new URLRequest ("sausage.png") );
			// when texture is loaded, achte auf den direkten Verweise von flash.events.Event.COMPLETE
			loader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE, onComplete );

			function onComplete( evt:flash.events.Event ):void
			{
				// grab the loaded bitmap
				var loadedBitmap:Bitmap = evt.currentTarget.loader.content as Bitmap;
				// create a texture from the loaded bitmap
				texture = Texture.fromBitmap(loadedBitmap);
				// erstelle ein Image object mit dieser einen texture
				var image:Image = new Image(texture);
				image.x = 20;
				image.y=50;
				addChild(image);

			}

		}
	}
}

Wurstsuppe animierte Würste

Beispiel

Startup Dokumentklasse siehe oben erstes Code beispiel
Game Klasse
CustomImage

Game

package 
{
	import flash.display.Bitmap;
	import starling.display.Image;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	import starling.utils.deg2rad;
	public class Game extends Sprite
	{
		private var sausagesVector:Vector.  = new Vector. (NUM_SAUSAGES,true);
		private const NUM_SAUSAGES:uint = 400;


		public function Game()
		{
			addEventListener(Event.ADDED_TO_STAGE,onAdded);
		}
		private function onAdded(e:Event):void
		{
			// erzeuge ein Texture object um das  Image object zu füttern
			//SausageData ist der Klassenname eines BitmapData Objektes. Es ist ein importiertes Bild
			//in der Bibliothek der fla mitExport für Actionscript und Klassenename SausageData
			var texture:Texture = Texture.fromBitmapData(new SausageData());
			for (var i:int = 0; i < NUM_SAUSAGES; i++)
			{
				// erstelle ein Image object mit dieser einen texture
				var image:CustomImage = new CustomImage(texture);
				// Zufallswerte für  alpha, position, rotation
				image.alpha = Math.random();
				// Startposition und Drehung zuweisen
				image.x = Math.random() * stage.stageWidth;
				image.y = Math.random() * stage.stageHeight;
				image.rotation = deg2rad(Math.random() * 360);
				image.destX = Math.random() * stage.stageWidth;
				image.destY = Math.random() * stage.stageHeight;

				// show it
				addChild(image);
				// Referencen auf die einzelnen images werden im Vector sausageVector gespeichert.
				sausagesVector[i] = image;
			}
			stage.addEventListener(Event.ENTER_FRAME, onFrame);
		}


		private function onFrame(e:Event):void
		{
			var lng:uint = sausagesVector.length;
			for (var i:int = 0; i < lng; i++)
			{
				// move the sausages around
				var sausage:CustomImage = sausagesVector[i];
				sausage.x -= ( sausage.x - sausage.destX ) * .01;
				sausage.y -= ( sausage.y - sausage.destY ) * .01;
				// when reached destination
				if ( Math.abs ( sausage.x - sausage.destX ) < 1 &&
				Math.abs ( sausage.y - sausage.destY ) < 1 )
				{
					sausage.destX = Math.random() * stage.stageWidth;
					sausage.destY = Math.random() * stage.stageHeight;
					sausage.rotation = deg2rad(Math.random() * 360);
				}
			}
		}

	}
}

CustomImage

package
{
    import starling.display.Image;
    import starling.textures.Texture;
    public class CustomImage extends Image
    {
        public var destX:Number = 0;
        public var destY:Number = 0;
        public function CustomImage(texture:Texture)
        {
            super(texture);
        }
    }
}

dgd

Hittest Collision Detection

Die Startling Hittest Funktion ist nicht identisch mit den AS3 Hittest Methoden.


public function hitTest(firstPoint: Point, firstAlphaThreshold: uint,
secondObject: Object, secondBitmapDataPoint: Point = null,
secondAlphaThreshold: uint = 1): Boolean

Der SecondObject Parameter kann ein Point, ein Rectangle oder ein BitmapData Object sein. Wenn es ein BitmapData Object ist, darf das Image Object nicht scaliert oder gedreht werden.

if ( sausageBitmapData1.hitTest(new Point(sausageImage2.x, sausageImage2.y), 255,
sausageBitmapData1, new Point(sausageImage1.x, sausageImage1.y), 255))
{
trace("Treffer");
}

Es folgt ein Beispiel. In dem Beispiel geht es darum image2 zu berühren, welche dann an die Position von image1 wechselt, woraufhin hittest true wird und image1 eine neue Position einnimmt.

Achtung, diese Klasse für sich alleine funktioniert nicht, sie muss, wie im ersten Beispiel in der Starling Klasse aufgerufen werden. Außerdem muss sich in der Bibliothek der fla ein Bild befinden mit "Export für Actionscript" und Klassenename SausageData

package 
{
	import flash.display.Bitmap;
	import starling.display.Image;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	import starling.utils.deg2rad;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import flash.geom.Point;
	import flash.display.BitmapData;


	public class Game extends Sprite
	{
		private var image1:Image;
		private var image2:Image;
		private var wurstData:BitmapData;
		var point1:Point = new Point(0,0);
		var point2:Point = new Point(0,0);


		public function Game()
		{
			addEventListener(Event.ADDED_TO_STAGE,onAdded);
		}
		private function onAdded(e:Event):void
		{

			//in der Bibliothek der fla liegt ein importiertes Bild, 
			//export für Actionscript, Name SausageData
			wurstData = new SausageData();
			var texture:Texture = Texture.fromBitmapData(wurstData);
			
			image1 = new Image(texture);
			newPosition();
			
			image2 = new Image(texture);
			image2.x = Math.random() * stage.stageWidth;
			image2.y = Math.random() * stage.stageHeight;


			addChild(image1);
			addChild(image2);
			image2.addEventListener(TouchEvent.TOUCH, onTouch);

		}

		private function onTouch(evt:TouchEvent):void
		{
			image2.x = image1.x;
			image2.y = image1.y;
			stage.addEventListener(Event.ENTER_FRAME, onFrame);
		}
		private function onFrame(event:Event):void
		{

			point1.x = image1.x;
			point1.y = image1.y;
			point2.x = image2.x;
			point2.y = image2.y;
			if (wurstData.hitTest(point2,255,wurstData,point1,255))
			{
				newPosition();
				stage.removeEventListener(Event.ENTER_FRAME, onFrame);
			}
		}
		
		private function newPosition():void
		{
			image1.x = Math.random() * stage.stageWidth;
			image1.y = Math.random() * stage.stageHeight;		
		}

	}
}

 

Im folgenden Beispiel bewegt sich ein Engel auf den anderen zu, sobald man ihn berührt. Wenn beide Engel kollidieren, fliegen sie in unterschiedliche Richtungen.

Beispiel Einer der beiden Engel muss berührt werden.

package 
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	import starling.utils.deg2rad;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import flash.geom.Point;

	
	public class Game extends Sprite
	{

		private var image1:CustomImage;
		private var image2:CustomImage;
		private var engelData:BitmapData;
		var point1:Point = new Point(0,0);
		var point2:Point = new Point(0,0);
		

		public function Game()
		{
			addEventListener(Event.ADDED_TO_STAGE,onAdded);
		}
		private function onAdded(e:Event):void
		{
			// in der Bibliothek der fla liegt ein Bild export für actionscript klassenname EngelData
			
			engelData = new EngelData();
			var texture:Texture = Texture.fromBitmapData(engelData);
			
			image1 = new CustomImage(texture);
			image1.x = Math.random() * stage.stageWidth;
			image1.y = Math.random() * stage.stageHeight;
			newDest(image1);
			
			image2 = new CustomImage(texture);
			image2.x = Math.random() * stage.stageWidth;
			image2.y = Math.random() * stage.stageHeight;



			addChild(image1);
			addChild(image2);
			//berührt man die große Wurst, bewegt diese sich auf die kleine zu
			image2.addEventListener(TouchEvent.TOUCH, onTouch);
	

		}
		private function onFrame(evt:Event):void
		{	
			collisionTest();
			//engel Bewegung
				var engel:CustomImage = (evt.currentTarget as CustomImage);
				engel.x -=  (engel.x - engel.destX) * .1;
				engel.y -=  (engel.y - engel.destY) * .1;
				// Wenn Zielpunkt erreicht, EventListener entfernen
				if (Math.abs(engel.x - engel.destX) < 1 && Math.abs(engel.y - engel.destY) < 1)
				{
					engel.removeEventListener(Event.ENTER_FRAME, onFrame);
				}
				
		}
		
		//wenn beide Engel sich berühren, bewegen sich sich auf 2 zufällige Positionen
		private function collisionTest():void
		{
			point1.x = image1.x;
			point1.y = image1.y;
			point2.x = image2.x;
			point2.y = image2.y;
			
			if (engelData.hitTest(point2,255,engelData,point1,255))
			{
				newDest(image1);
				image1.addEventListener(Event.ENTER_FRAME, onFrame);
				newDest(image2);
				image2.addEventListener(Event.ENTER_FRAME, onFrame);
			}
			
		}
		
		private function newDest(engel:CustomImage):void
		{
			engel.destX = Math.random() * stage.stageWidth;
			engel.destY = Math.random() * stage.stageHeight;
		}
		
		private function onTouch(evt:TouchEvent):void
		{
			image2.destX = image1.x;
			image2.destY = image1.y;
			image2.addEventListener(Event.ENTER_FRAME, onFrame);
		}	
		
	}
}

Custom Image Klasse
Hierzu gehört auch noch eine Klasse CustomImage, in welcher die destX und destY Parameter definiert sind. Außerdem noch die Startup Klasse siehe erstes Codebeispiel.

package 
{
	import starling.display.Image;
	import starling.textures.Texture;
	public class CustomImage extends Image
	{
		public var destX:Number = 0;
		public var destY:Number = 0;
		public function CustomImage(texture:Texture)
		{
			super(texture);
		}
	}
}

Drawing API

Es gibt in Starling keine Drawing API. Stattdessen kann man die Drawing Api von Flash nutzen und dann daraus ein BitmapData Objekt erstellen. Siehe meinen Tipp Bitmap. Dort wird die BitmapData.draw Methode erklärt.

var meinBitmapData:BitmapData = new BitmapData(110,100);
meinBitmapData.draw(farben_mc);

Es folgt ein Beispiel:

Achte im folgenden Beispiel auf die Kleinigkeiten in Punkto Maße, ohne Rand wäre hier einiges einfacher. Jedoch die 4 Pixel Rand müssen bei der Positionierung und Größe berücksichtigt werden.

package 
{
	import flash.display.BitmapData;
	import starling.display.Image;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	import flash.display.Bitmap;

	public class Game extends Sprite
	{
		private const NUM_CIRCLES:uint = 400;
		public function Game()
		{

			addEventListener(Event.ADDED_TO_STAGE, onAdded);
		}
		private function onAdded(e:Event):void
		{
			// Achte hier auf die genaue Zuweisung flash.display.Sprite()
			//denn es gibt auch starling Sprite
			var shape:flash.display.Sprite = new flash.display.Sprite();
			// radius
			var radius:uint = 100;		
			shape.graphics.beginFill(0xFFFF00);
			shape.graphics.lineStyle(4, 0x000000, .75); 
			// circle Argumente drawCircle(x, y, radius), Mittelpunkt in der Mitte
			shape.graphics.drawCircle(radius+2,radius+2,radius);
			shape.graphics.endFill();
			//radius plus Linienbreite
			radius = 104;
			
			var bmd:BitmapData = new BitmapData(radius * 2, radius * 2, true, 0);
			//draw Methode zeichnet die gerade erstellte Circle Grafik
			bmd.draw(shape);						
			// Texture von Bitmap Data
			var texture:Texture = Texture.fromBitmapData(bmd);
			// bild aus texture erstellen
			var image:Image = new Image(texture);
			
			//Das Bild mit Farben versehen 
			//im Uhrzeigersinn kann man in jeder Ecke eine Farbe definieren
			image.setVertexColor(0, 0xA00000);
			image.setVertexColor(1, 0xA00000);
			//image.setVertexColor(2, 0xFFFF00);
			//image.setVertexColor(3, 0xFFFF00);
			image.x = image.y = 20;
			addChild(image);

		}
	}
}


 

Im folgenden Beispiel werden viele bunte Kreise erzeugt. Beachte, dass alle image Objekte auf ein Texture Objekt zugreifen. Würden die BitmapData und Texture Objekte alle in der For-Schleife neu instanziiert, würde die Performance darunter leiden.

package 
{
	import flash.display.BitmapData;
	import starling.display.Image;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	public class Game extends Sprite
	{
		private const NUM_CIRCLES:uint = 400;
		public function Game()
		{

			addEventListener(Event.ADDED_TO_STAGE, onAdded);
		}
		private function onAdded(e:Event):void
		{
			// Achte hier auf die genaue Zuweisung flash.display.Sprite()
			//denn es gibt auch starling Sprite
			var shape:flash.display.Sprite = new flash.display.Sprite();
			// radius
			var radius:uint = 20;
			//  color fill
			shape.graphics.beginFill(0xFFFFFF);
			// circle Argumente drawCircle(x, y, radius), Mittelpunkt in der Mitte;
			shape.graphics.drawCircle(radius,radius,radius);
			shape.graphics.endFill();
			// erzeuge ein BitmapData buffer;
			var bmd:BitmapData = new BitmapData(radius * 2, radius * 2, true, 0);
			// draw Methode zeichnet die gerade erstellte Circle Grafik
			bmd.draw(shape);
			// create a Texture out of the BitmapData
			var texture:Texture = Texture.fromBitmapData(bmd);
			for (var i:int = 0; i < NUM_CIRCLES; i++)
			{
				// create an Image out of the texture
				var image:Image = new Image(texture);
				image.x = Math.random() * stage.stageWidth;
				image.y = Math.random() * stage.stageHeight;
				image.color = Math.random() * 0xFFFFFF;
				image.scaleX = image.scaleY = Math.random();
				// show it!
				addChild(image);
			}
		}
	}
}

MovieClips

Um einen Starling Movieclip zu erstellen benötigen wir ein Sprite Sheet. Das ist ein Bild, welches aufgeteilt ist und aus lauter Einzelbildern besteht. Die Anordnung der Einzelbilder wird in einer XML oder JSON Datei gespeichert. Mit einem Programm wie Texture Packer lassen sich derlei Sprite Sheets erstellen. Aber auch in Flash CS6 ist es möglich einen Movieclip mit einer Zeitleistenanimation als SpriteSheet für Starling zu erzeugen. Auch dieses Videotutorial von Hemanth Sharma erklärt die SpriteSheet Exportfunktion in Flash CS6.

Wähle einen oder mehrere Movieclips in der Bibliothek aus und wähle aus dem Kontextmenü: Spritesheet erstellen. Wähle unter Datenformat "Starling". Voila es entstehen eine Xml Datei und ein png.

Nun geht es weiter mit Flash Builder. Wenn Sie Flash Builder 4.5 haben, müssen Sie dieses erst einrichten siehe oben.

Erzeugen Sie ein neues Actionscript Projekt. (in Flash Builder 4.5, wählen Sie die eingefügte neue Flex SDK Version).

Quellpfad, Starling Framework auswählen.

Es folgt die Dokumentenklasse: StarlingSetup

package
{
	import flash.display.Sprite;
	import starling.core.Starling;
	
	[SWF(width="1280", height="752", frameRate="60", backgroundColor="#002143")]
	public class StarlingSetup extends Sprite
	{
		public function StarlingSetup()
		{
			var _st:Starling = new Starling(Game, stage);
			_st.start();
		}
	}
}

Es folgt die Klasse für das Spritesheet: Game

package
{
	import flash.display.Bitmap;
	
	import starling.display.MovieClip;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	import starling.textures.TextureAtlas;
    import starling.core.Starling;
	import starling.animation.Juggler;

	public class Game extends Sprite
	{
		private var mMovie:MovieClip;
		[Embed(source="mySpritesheet.xml",mimeType="application/octet-stream")]
		public static const SpriteSheetXML:Class;
		[Embed(source="mySpritesheet.png")]
		private static const SpriteSheet:Class;
		public function Game()
		{
			addEventListener(Event.ADDED_TO_STAGE, onAdded);
		}
		private function onAdded (e:Event):void
		{
			// das eingebettete bitmap (die spritesheet png Datei) wird instanziiert
			var bitmap:Bitmap = new SpriteSheet();
			// ein Texure Objekt wird daraus erzeugt
			var texture:Texture = Texture.fromBitmap(bitmap);
			//die XML Datei, welche die Detailinfos der Frames im Spritesheet enthält, wird erzeugt
			var xml:XML = XML(new SpriteSheetXML());
			// creates a texture atlas mit den Argumenten( texture/spritesheet , XML Beschreibung)
			var sTextureAtlas:TextureAtlas = new TextureAtlas(texture, xml);
			// ein Vector welches die Frames bekommt, in meinem Beispiel beginngen alle Bilder mit mySpritesheet
			var frames:Vector. = sTextureAtlas.getTextures("mySpritesheet");
			// erzeugt MovieClip  mit 40fps
			mMovie = new MovieClip(frames, 40);
			// Movieclip wird positoniert
			mMovie.x = stage.stageWidth - mMovie.width >> 1;
			mMovie.y = stage.stageHeight - mMovie.height >> 1;
			// zur Displayliste hinzufügen
			addChild ( mMovie );
			//mMovie.currentFrame = 20;
            Starling.juggler.add(mMovie);
            //mMovie.pause(); mMovie.play(); mMovie.currentFrame = 20;

		}		
	}
}

Die XML Datei und die png Datei müssen in den "src" Unterordner des Projekts geschoben werden. Sie heißen in meinem Beispiel
mySpritesheet.png
mySpritesheet.xml

In der XML Datei sind Verweise auf die einzelnen Bilder. Sie beginnen in meinem Beispiel alle mit mySpritesheet, gefolgt von einer Zahl. Dieses Präfix wird auch in Actionscript benötigt, um auf die Bilder des Movieclips zu verweisen.

var frames:Vector. = sTextureAtlas.getTextures("mySpritesheet");

Es ist auch möglich und von Vorteil mehrere verschiedenen Animationen mit unterschiedlichen Namen in einem Spritesheet zu hinterlegen. Man findet dazu zahlreiche Tipps im Netz.

Der MovieClip wird abgespielt, wenn man ihn mit Juggler Klasse hinzugefügt hat.

Siehe auch in den Referenzen die Möglichkeiten, die die MovieClip Klasse von Starling bietet. Man kann Frames hinzufügen oder entfernen. Man kann einzelnen Frames eine individuelle Dauer zuweisen und vieles mehr.

Movieclip in der Flash IDE

Es ist auch möglich das gleiche in der Flash IDE zu machen. Dazu solltet ihr die png Datei in die Biblitothek importieren. Export für Actionscript, Klassenname SpriteData

Um die Ladeprozedur der XML Datei zu umgehen, habe ich einfach die XML Struktur direkt in die Actionscript Klasse eingefügt.Die Dokumentklasse entspricht dem vorigen Beispiel. Es folgt das Beispiel der Game Klasse. Anstelle der XML Daten habe ich einen Hinweis eingefügt der muss durch die XML Daten ersetzt werden siehe meinen XML Tipp. Das beginnt folgendermaßen:

var xml:XML = <TextureAtlas imagePath="mySpritesheet.png">

package
{
	import flash.display.Bitmap;
	
	import starling.display.MovieClip;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	import starling.textures.TextureAtlas;
	import flash.display.BitmapData;
    import starling.animation.Juggler;
	import starling.core.Starling;

	public class SpriteSheetExample extends Sprite
	{
		private var mMovie:MovieClip;
		
		public function SpriteSheetExample()
		{
			addEventListener(Event.ADDED_TO_STAGE, onAdded);
		}
		private function onAdded (e:Event):void
		{
			
			// das eingebettete bitmap (die spritesheet png Datei) wird instanziiert
			var bitmap:Bitmap = new Bitmap(new SpriteData());
			
			// ein Texure Objekt wird daraus erzeugt
			var texture:Texture = Texture.fromBitmap(bitmap);
			//die XML Datei, welche die Detailinfos der Frames im Spritesheet enthält, wird erzeugt
			
			var xml:XML = !!!! hier kommen die XML Daten rein !!!!!! 
			
			
			// creates a texture atlas mit den Argumenten( texture/spritesheet , XML Beschreibung)
			var sTextureAtlas:TextureAtlas = new TextureAtlas(texture, xml);
			// ein Vector welches die Frames bekommt, in meinem Beispiel beginngen alle Bilder mit mySpritesheet
			var frames:Vector. = sTextureAtlas.getTextures("mySpritesheet");
			// erzeugt MovieClip  mit 40fps
			mMovie = new MovieClip(frames, 40);
			// Movieclip wird positoniert
			mMovie.x = stage.stageWidth - mMovie.width >> 1;
			mMovie.y = stage.stageHeight - mMovie.height >> 1;
			// zur Displayliste hinzufügen
			addChild ( mMovie );			
            Starling.juggler.add(mMovie);	

		}		
	}
}

Optimierung

Es macht Sinn alle Animationen in einer Textur Datei abzulegen. Die Anzahl der uploads sinkt. Auch das Wechseln von einer zu anderen Textur braucht Zeit. Außerdem ist es praktisch.

CPU und GPU

Graphic Processor Unit und Central Processor Unit. Wenn es um Optimierung der Performance geht, gibt es 2 Gründe, die das verlangsame. Die CPU wird überlastet und die GPU muss auf die CPU warten oder umgekehrt.
CPU macht eine Menge in Sachen (physic, komplexe Spiel Logik etc) Es ist also wichtig das GPU und CPU schön aufeinander abgestimmt arbeiten.

Um herauszubekommen, wo die meiste Zeit im Code verbraucht wird, hat Adobe einen legen(warte es kommt gleich)deren Profiler entwickelt, genannt AdobeScout

ActionScript Optimierung

Der Garbage Collector (GC) wirkt sich stark auf die Performance aus. Jedesmal wenn er ausgelöst wird, gibt es eine Unterbrechung, die eine flüssige Performance verhindert. Also sollte man nicht jede Menge Objekte zerstören und wieder neu erschaffen, sondern stattdessen sowenig wie möglich Objekte im Speicher haben, die bei Bedarf eingesetzt werden können. Auf der anderen Seite darf natürlich nicht alles im Speicher bleiben. Eine ausgewogene Balance ist hier wichtig. So sollte man beispielsweise in Schleifen (for-Schleifen) bei jedem Schleifendurchlauf neue Objekte erzeugen, wenn es auch möglich wäre immer auf der gleiche Objekt (mit eventuell geänderten Eigenschaften) zuzugreifen.

FALSCH

const MAX_NUM:int = 18;
const COLOR:uint = 0xCCCCCC;
var area:Rectangle;
for (var:int = 0; i < MAX_NUM; i++)
{
//Do not use the following code
area = new Rectangle(i,0,1,10);
myBitmapData.fillRect(area,COLOR);
}

RICHTIG

 

const MAX_NUM:int = 18;
const COLOR:uint = 0xCCCCCC;
// Create the rectangle outside the loop
var area:Rectangle = new Rectangle(0,0,1,10);
for (var:int = 0; i < MAX_NUM; i++)
{
area.x = i;
myBitmapData.fillRect(area,COLOR);
}

Pooling

Ein anderes Szenario. Man erstellt eine Anzahl von Objekten und hinterlegt sie beispielsweise in einem Vektor oder Array. Wenn man mit dem Objekt fertig ist, deaktiviert man es, so dass es keine CPU Ressourcen beansprucht. Man entfernt alle Referenzen, aber man setzt die Referenzen nicht auf null, denn erst dann könnte der GC sie entfernen. Wenn man das Objekt wieder braucht, bringt man es zurück in den Pool.

Schleifen

For Schleifen, sollte man bevorzugt einsetzen. Es macht Sinn die Länge abzuspeichern, als sie in jedem Schleifendurchlauf erneut zu evaluieren.

// langsam:
for each (var item:Object in vector){ ... }
// besser:
for (var i:int=0; i < vector.length; ++i) { ... }
// am schnellsten:
var length:int = vector.length;
for (var i:int=0; i < length; ++i) { ... }

Wenn man einen Index im Array oder Vector benutzt, sollte man diesen als int casten.

// bad:
var element:Sprite = vector[10*x];
// better:
var element:Sprite = vector[int(10*x)];

Vector ist schneller als Array

Button

Starling besitzt eine Button Klasse, die sich von der Flash SimpleButton Klasse unterscheidet. Man kann 2 Bilder für 2 Zustände definieren und man kann einen Text hinzufügen.

public function Button(upState:Texture, text:String="", downState:Texture=null)

Es folgt ein Beispiel für die Flash IDE. Man kann dieses Beispiel aber auch mit Flashbuilder benutzen, die dafür vorgesehenen Codestellen sind auskommentiert.

package 
{
	import flash.display.Bitmap;
	import starling.display.Button;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.textures.Texture;
	public class Game extends Sprite
	{
		//eingebettet wenn mit Flashbuilder erstellt
		/*[Embed(source = "../media/textures/button_normal.png")]
		private static const ButtonTexture:Class;*/
		public function Game()
		{
			addEventListener(Event.ADDED_TO_STAGE, onAdded);
		}
		private function onAdded(e:Event):void
		{
			// folgender Code wenn Flashbuilder
			//var buttonSkin:Bitmap = new ButtonTexture(new But1());

			//Flash IDE Bild in Bibliothek BitmapData, Name: But1
			var buttonSkin:Bitmap = new Bitmap(new But1());

			// create a Texture object to feed the Button object
			var texture:Texture = Texture.fromBitmap(buttonSkin);
			// create a button using this skin as up state
			var myButton:Button = new Button(texture,"Play");
			// createa container for the menu (buttons)
			var menuContainer:Sprite = new Sprite();
			// add the button to our container
			menuContainer.addChild(myButton);
			// centers the menu;
			menuContainer.x = stage.stageWidth - menuContainer.width >> 1;
			menuContainer.y = stage.stageHeight - menuContainer.height >> 1;
			// show the button
			addChild(menuContainer);
		}
	}
}