Tag Archive for 'ActionScript'

Tipp Flash CS4: ActionScript Vector und Compiler-Konstanten

ActionScript ist mit Flash CS4 Professional trotz der in den Medien kursierenden Diskussionen rund um die ActionScript-Basis ECMAScript 4 weiter gewachsen (http://www.flashforum.de/forum/showthread.php?t=257795 und http://blogs.adobe.com/open/2008/08/blog_entry_dated_81408_715_pm.html). Die spektakulärste Neuerung aus Sicht von Programmierern könnte dabei der neue Datentype Vector sein. Dieser erlaubt erstmals typisierte und damit typsichere Listen. Die Verwendung geschieht analog zur Klasse Array. Einzig die Typisierung und das Erstellen bedürfen einer neuen Schreibweise mit Angabe des Typs in spitzen Klammern getrennt durch einen Punkt:

1
2
var v:Vector.<String>;
v = new Vector.<String>();

Zu den Neuerungen gehören außerdem die Klassen rund um Text, Pixel Bender (Codename Hydra), 3D und inverse Kinematik. Außerdem kann man nun Sounds zur Laufzeit erzeugen, primitive Formen leichter mit der Graphics-Klasse zeichnen und Dateien auch aus dem Browser heraus per FileReferenc lokal laden und speichern. Und in der Bibliothek ist endlich sichtbar, ob es sich um einen MovieClip, ein Sprite oder ein mit dem Flex Integration Kit für Flash aufbereitetes Symbol handelt. Bei der Zusammenarbeit mit Flex hilft es außerdem sehr, dass Flash nun endlich auch Metadaten wie AccessibilityClass, ResourceBundle, Style, Embed und SWF versteht – nicht nur in Klassen sogar in Bildskripts. Gerade Embed ist auch in reinen Flash Projekten durchaus nützlich, um Inhalte allein durch die Programmierung und ohne den Umweg über die Bibliothek zu nutzen.
Die Einstellungsmöglichkeiten von ActionScript umfassen nicht mehr nur eine Sammlung von Klassenpfaden sondern sind ähnlich wie bei Flex in Quellcode-Pfade, externe Bibliotheken zur Laufzeit (Runtime Shared Libraries) und Bibliohtekspfade zur Kompilierzeit unterteilt. Hinter den ActionScript-Einstellungen im Register Flash der Einstellungen für das Veröffentlichen im Menü Datei verbergen sich nicht nur die Klassenpfade. Dort können auch Konstanten angelegt werden, die wie normale Variablen in ActionScript zu nutzen sind. Außerdem erlauben diese auch eine bedingte Kompilierung, wenn der relevante Code hinter der Konstante in geschweiften Klammern steht:

1
2
3
4
CONFIG::FLASH_AUTHORING {
	trace("Foo");
}
trace(CONFIG::FLASH_AUTHORING);

Flash Checker, Teil 2: Objektorientiertes JSFL

Im ersten Teil des Flash Checkers habe ich eine einfache Flash-Erweiterung verfasst, die die Dokumenteinstellungen einer FLA-Datei prüft. Dem einen oder anderen mag es übel aufstoßen, dass diese Erweiterung für die Flash Entwicklungsumgebung in JavaScript verfasst wurde. Mal abgesehen davon, dass – wer unbedingt will – auch DLLs programmieren und als Erweiterung einbinden kann, ist JavaScript jedoch besser als sein Ruf: Beispielsweise die objektorientiere Programmierung ist durchaus möglich. Wenn auch nur Prototypen basiert. Das heißt, dass man bei JavaScript die Vererbung per Hand über sogenannte Prototypen erreicht. Denn jedes Objekt in der JavaScript-Welt verfügt über eine Prototyp-Eigenschaft, die auf ein anderes Objekt verweist. Und sollte eine Methode oder Eigenschaft nicht in einem Objekt verfügbar sein, dann wird halt im Prototyp nachgeschaut. Und sollte in diesem Objekt die Methode oder Eigenschaft ebenfalls nicht verfügbar sein, dann wird halt in dessen Prototyp nachgeschaut. Und so weiter und so fort…

Das Pendant zu einer Klasse erstellen Sie in JavaScript mit einer Funktion.

1
2
3
function FlashChecker () {
	this.property = "foo";
}

Eine Instanz dieser „Klasse“ erzeugt das Schlüsselwort new, in dem erst ein neues Objekt angelegt und die Funktion ähnlich einem Konstruktor im Kontext dieses neuen Objektes ausgeführt wird (das Schlüsselwort this bezieht sich in diesem Fall auf das gerade neu erzeugte Instanzobjekt).

1
var checker = new FlashChecker();

Es ist gängige Praxis, die Methoden einer „Klasse“ in das Prototyp-Objekt zu schreiben, damit die Methoden nur einmal deklariert und nur einmal im Speicher abgelegt werden.

1
2
3
4
5
6
7
8
9
10
11
12
FlashChecker.prototype.checkDocumentSettings = function (currentDocument) {
	var playerVersion = currentDocument.getPlayerVersion();
	var asVersion = currentDocument.asVersion;
	var fps = currentDocument.frameRate;
	var height = currentDocument.height;;
	var width = currentDocument.width;
	if (playerVersion != "9") fl.trace("Warning: Wrong player version!");
	if (asVersion != 3) fl.trace("Warning: Wrong ActionScript version!");
	if (fps != 24) fl.trace("Warning: Wrong framerate!");
	if (height != 800) fl.trace("Warning: Wrong height!");
	if (width != 800) fl.trace("Warning: Wrong width!");	
}

Die Instanzen haben dann über den Prototyp implizit (also ohne dass man es extra hinschreiben muss) per __proto__-Eigenschaft Zugriff darauf, so dass der Verwendung durch die Instanzen nichts im Wege steht.

1
2
3
var checker = new FlashChecker();
var currentDocument = fl.getDocumentDOM();
checker.checkDocumentSettings(currentDocument);

Rundungsfehler im Flash Player?

Bei der Aufzeichnung unseres aktuellen Trainings kam Saban auf mich zu, da seine 3D-Versuche mit Rundungsfehlern zu kämpfen hätten. Und in der Tat, Math.sin(Math.PI) ergibt nicht die zu erwartende 0. Aber warum ist dieses Problem in älteren Projekten nie aufgefallen?

Meine Tests ergaben, dass Math.sin(Math.PI) nicht mehr nur 15 signifikante Stellen (wie z. B. auch Math.PI) sondern im aktuellen Flash Player ein paar Nachkommastellen mehr hat. Wenn ich die Genauigkeit nun einfach wieder auf das Verhalten älterer Flash Player ändere und Math.round(Math.sin(Math.PI)*1e15)/1e15 nutze, dann kommt auch wie erwartet 0 heraus. Eigentlich ist dieses Ergebnis ein gutes Zeichen, da die vom Flash Player verwendete Näherung für Sinus und Cosinus nun deutlich genauer zu arbeiten scheint (also mit mehr Stellen). Doch im Einzelfall kann diese Näherung auf Dauer zu Berechnungsfehlern führen, die es früher nicht gab – gerade bei umfangreichen 3D-Berechnungen sollte man das im Auge behalten.

Das Berechnen von Werten scheint sich aber auch an anderen Stellen seltsam zu verhalten. Bei Saban ergab beispielsweise trace(4.4 + 2.9) im Flash Player 10 den Wert 7.300000000000001. Woher auch immer die Nachkommastelle kommt. Und bei einem eigenen Versuch wurde aus 1.0000000000000005 (also der 5 auf der 16. Nachkommstelle) die Zahl 1.00000000000000004 (also eine 4 auf der 16. Nachkommstelle). Mit ActionScript 2 (also der alten AVM) passen die Werte jedoch auch im Flash Player 10 bzw. werden korrekt gerundet.

Flash Checker, Teil 1: JavaScript Flash (JSFL)

Ich weiß nicht warum, aber irgendwie habe ich es in den letzten Jahren versäumt, die ganzen kleinen JavaScript-Erweiterungen für Flash (JSFL) zu sammeln, die bei den verschiedenen Projekten so entstanden sind. Nun bin ich in einem großen Projekt engagiert, in dem viele externe Entwickler und Agenturen Flash-Dateien anliefern, die möglichst automatisch geprüft werden sollen und da nutze ich doch die Chance das nachzuholen. Zumal die Prüfung von Flash-Dateien (FLA) ja nicht das einzige ist, was sich damit erledigen lässt: Einige Kunden nutzen JSFL sogar, um Prozesse zu automatisieren und z. B. Teile eines Projektes automatisch zu generieren. Gerade bei sehr gleichförmigen Inhalten lässt sich so sehr viel Zeit sparen.

Wer selber in das Thema einsteigen möchte, kann sich im Verlauf-Bedienfeld (History-Panel) nicht nur seine Aktionen in Flash, sondern auch die dahinter versteckten JavaScript-Befehle anzeigen lassen. Wenn man das dann noch über das Menü „Befehle“ (Commands) ähnlich einem Makro aufzeichnet, dann erhält man eine fertige JSFL-Datei, die sich im Benutzerverzeichnis für Flash findet: Bei Flash CS3 unter Windows Vista ist das meist C:\Users\[Benutzername]\AppData\Local\Adobe\Flash CS3\[Sprache]\Configuration\Commands. Und an diesem Ort kann man gleich auch selbst verfasste Befehle als JSFL-Datei ablegen (oder automatisch als Extension installieren lassen), die dann ebenfalls im Befehle-Menü erscheinen.

Ein einfacher Befehl könnte z. B. grundsätzliche Einstellungen wie die Flash Player-Version und ActionScript-Variante, die Dokumentgröße und die Bildrate prüfen und Abweichungen im Ausgabefenster (Output) anzeigen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Clear output panel
fl.outputPanel.clear(); 
 
// Check Flash Player Version
var playerVersion;
playerVersion = fl.getDocumentDOM().getPlayerVersion();
if (playerVersion != "9" ) fl.trace("Warning: Export should be for Flash Player 9!");
 
// Check ActionScript version
var asVersion;
asVersion = fl.getDocumentDOM().asVersion;
if (asVersion != 3) fl.trace("Warning: Use ActionScript 3 and not " + asVersion + "!");
 
// Check framerate
var fps;
fps = fl.getDocumentDOM().frameRate;
if (fps != 24) fl.trace("Warning: Framerate should be 24 but it is " + fps + "!");
 
// Check document size
var height;
var width;
height = fl.getDocumentDOM().height;
width = fl.getDocumentDOM().width;
if ( height != 600 || width != 800 ) fl.trace("Warning: Document size should be 800 x 600 and not " + width + " x " + height + "!");

Der nächste Teil des „Flash Checkers“ soll Die nächsten Teile des “Flash Checkers” werden sich um die objektorientierten Programmierung und um die Innereien wie Zeitleiste und Bibliothek kümmern. Über Anregungen würde ich mich sehr freuen…

ActionScript 3 Preloader mit Framefactory

English: The following article describes the ActionScript 3 metadata framefactory and its usage for creating a preloader.

Deutsch: Zahlreiche Wege führen zu einer Flash-Anwendung. Und der Weg über reinen Code ganz ohne Zeitleiste erfreut sich dabei zunehmender Beliebtheit – zumal mit dem Flash Development Toolkit (FDT) und dem Flex Builder zwei Eclipse basierte Entwicklungsumgebungen bereitstehen, die dem im Flash enthaltenen Editor Lichtjahre voraus sind. Doch diese Herangehensweise hat auch ein paar Tücken. Eine davon ist der Preloader, der bei größeren Anwendungen den Ladeprozess ansprechend visualisiert.

In klassischen Flash-Anwendungen wird vielfach die Zeitleiste für den Preloader missbraucht, indem am Anfang die Wiedergabe stoppt, bis der restliche Inhalt geladen ist – dafür muss natürlich sichergestellt sein, dass so viel Inhalt wie möglich erst in den späteren Bereichen der SWF-Datei liegt (was sich übrigens gut mit dem Bandbreiten-Profiler in Flash prüfen lässt). So ganz ohne Zeitleiste klappt das mit FDT oder dem Flex Builder natürlich erst einmal nicht. Doch ein Blick in das Flex Framework zeigt, dass dort eine Lösung namens Framefactory exisitert. Denn das Metadata [Frame(factoryClass=”Class”)] erlaubt es, Code explizit an den Anfang der SWF-Datei zu legen und diesen z. B. für einen Preloader zu nutzen, bevor der restliche davon unabhängige Inhalt geladen wird.

1
2
3
4
5
6
7
package {
	import flash.display.MovieClip;
	[Frame(factoryClass="biz.wolter.app.manager.SystemManager")]
	[SWF(width="800", height="600", frameRate="24", backgroundColor="#999999", pageTitle="My Rich Application")]
	public class Application extends MovieClip {
	}
}

Die in der Framefactory referenzierte Klasse (hier analog zu Flex der SystemManager) enthält nun allen Code für den Preloader und die Initialisierung der Anwendung. Letzteres darf aber nicht durch eine „konkrete“ Referenzierung erfolgen, da ja dann der zugehörige Code mit dem SystemManager an den Anfang der SWF-Datei gepackt würde. Deshalb kommt hier nach einem erfolgreichen Ladevorgang die Methode getDefinitionByName zum Einsatz.

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
31
32
33
34
35
36
37
38
39
40
41
package biz.wolter.app.manager {
	import biz.wolter.app.controls.Preloader;
 
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.utils.getDefinitionByName;
 
	public class SystemManager extends MovieClip {
 
		public function SystemManager() {
			stop();
			stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
			// Custom Preloader
			Preloader.show();
		}
 
		/*
		 * Handle the loading progress
		 */
		public function onEnterFrame(event:Event):void {
			if(framesLoaded == totalFrames) {
				stage.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
				Preloader.hide();
				nextFrame();
				create();
			} else {
				// nothing, cause Preloader handles itself
			}
		}
 
		/*
		 * Finally create the application
		 */
		private function create():void {
			var mainClass:Class = Class(getDefinitionByName("Application"));
			var app:Object = new mainClass();
			addChildAt(app as DisplayObject,0);
		}
	}
}