18. září 2008

Když vázne komunikace mezi flash animacemi při více otevřených oknech prohlížeče

Komunikace mezi více Flash animacemi, které jsou umístěny na jedné HTML stránce se dá velmi elegantně řešit pomocí třídy Action Sriptu LocalConnection. Je však nutné ošetřit případ, kdy bude stejná HTML stránka otevřená ve více oknech. Jedno z řešení tohoto problému popisuji níže.

Jak se komunikace mezi SWF v jedné HTML stránce vlastně naprogramuje?

Představte si, že máte na stejné HTML stránce vložené dvě Flash animace (movie_1.swf a movie_2.swf) a chcete je navzájem synchronizovat, tzn. že animace z movie_2.swf se má spustit až v okamžiku, kdy animace v movie_1.swf dospěje do určité fáze - dejme tomu až přehrávání animace doběhne k snímku č.79.

Pomocí třídy Action Scriptu LocalConnection to vyřešíte tak, že do movie_1.swf na snímek č.79 vložíte následující kód:

var LC_12:LocalConnection = new LocalConnection();
LC_12.connect("CN_12");
LC_12.send("CN_12","FN_receiver_12");

a do movie_2.swf na první snímek tento kód:

stop();
var LC_12:LocalConnection = new LocalConnection();
LC_12.connect("CN_12");
LC_12.FN_receiver_12 = function()
{
_root.gotoAndPlay(2);
};

Přepodkládám přitom, že obě animace probíhají na hlavní časové ose, že animace v movie_1.swf se spustila a že animace v movie_2.swf začíná až na snímku č.2. Jiný způsob užití stejného postupu najdete např. v článku Komunikace mezi dvěma SWF soubory.

Je to jednoduché a jasné - prostě skvělé pro použití. Zádrhel je ovšem v tom, že když tutéž HTML stránku se zmíněnými Flash animacemi otevřete ve více oknech, budou všechny instance LocalConnection shodné napříč všemi okny. To, že se budou jmenovat stejně nevadí. Vadí to, že volají i naslouchají na stejné "telefonní lince", jejíž "číslo" jsem v AS nastavil natvrdo na "CN_12" - okno, neokno. Tím ovšem příjímač ve všech otevřených movie_2.swf totálně zmatu.

Co s tím?

Chtělo by to prostě pro každé okno unikátní "telefonní číslo". Pokud HTML stránku generujete dynamicky, nebo se můžete spolehnout na pomoc Java Scriptu, máte vyhráno. Stačí, když do tagu OBJECT, kterým vkládáte každou z obou zméněných Flash animací do HTML stránky, připojíte za název SWF souboru proměnnou, jejícž hodnotu nastavíte pro obě animace v téže HTML stránce sice shodně, ale okno od okna unikátně.

Proměnná se může jmenovat např. "cnprefix" a v HTML stránce pak bude na místě vložení SWF tento kód:

movie_1.swf?cnprefix=abcd

a

movie_2.swf?cnprefix=abcd

Pozor! Hodnota proměnné "cnprefix" musí být v jedné HTML stránce stejná, ovšem různá v různých oknech s toutéž stránkou. Asi nejbezpečnější je vkládat hodnotu časového razítka sečtenou s nějakým náhodně vygenerovanýcm řetězcem, což zaručí neopakovatelnost. Takže v jednom okně najdneme např.:

movie_1.swf?cnprefix=abcd2368358479
movie_2.swf?cnprefix=abcd2368358479

v druhém

movie_1.swf?cnprefix=xac2368358479
movie_2.swf?cnprefix=xac2368358479

(i když, jak to mohl vlastně uživatel stihnout, že) a v třetím

movie_1.swf?cnprefix=efg2368358521
movie_2.swf?cnprefix=efg2368358521

V obou Flash animacích se pak trochu změní kód Action Scriptu. V movie_1.swf bude:

var LC_12:LocalConnection = new LocalConnection();
LC_12.connect(_root.cnprefix + "CN_12");
LC_12.send(_root.cnprefix + "CN_12","FN_receiver_12");

a v movie_2.swf:

stop();
var LC_12:LocalConnection = new LocalConnection();
LC_12.connect(_root.cnprefix + "CN_12");
LC_12.FN_receiver_12 = function()
{
_root.gotoAndPlay(2);
};

Proč pro hodnotu "telefonnícho čísla" používám součet řetězců _root.cnprefix + "CN_12" a ne přímo hodnotu _root.cnprefix? Je to proto, že v rámci obou animací mohu chtít pro jiné účely vytvářet další instance LocalConnection a každá z nich musí používat unikátní "telefonní číslo".

Z důvodů přehlednosti se mi osvědčil postup, kdy jednotlivé komunikující dvojice instancí LocalConnection používají "telefonní čísla" jichž hodnota vyjadřuje, odkud kam se volá, tzn. např.: CN_12, CN_21, CN_21_A, CN_21_B atd. Tím mám zajištěnou unikátnost "telefonních linek" v rámci jedné HTML stránky a připojení hodnoty "cnprefix" jako předpony (proto tedy prefix) k všem "telefonním číslům" mi zas zaručuje unikátnost "linek" v jednotlivých oknech prohlížeče navzájem.

12. září 2008

Jak jednoduše převést jakékoli reálné číslo na celé, které bude ležet v povoleném intervalu hodnot

Pokud ve Flash animaci potřebujete ošetřit vstup číslené hodnoty tak, abyste měli k dispozici číslo, které bude vždy celé a bude spadat do souvislého intervalu ostře omezeného hodnotami "n1" a "n2" (tzn. že tyto vymezující hodnoty patří do množiny povolených čísel), nemusíte v Action Scriptu psát žádnou složitou testovací proceduru.

Vystačíte si s jedním řádkem kódu, který jakékoli reálné číslo "x" převede na číslo celé "n" a pokud je toto zaokrouhlené číslo vně intervalu "n1" až "n2", přiřadí mu nejbližší krajní hodnotu:

n = Math.min(Math.max(n1, Math.floor(x)), n2);

Místo Math.floor(x) můžete samosebou použít Math.ceil(x) nebo Math.round(x), podle toho, jaký druh systematické chyby na vstupu předpokládáte.

Navíc zápis Math.min(Math.max(n1, n), n2); má sám o sobě elegantní tvar, který se nejen dobře pamatuje a zapisuje, ale má i jasnou vypovídací hodnotu.

2. září 2008

Pořadí změny rozměrů instance movieklipu a nastavení její rotace

Taková banalita a potrápila mne skoro hodinu, než jsem si uvědomil v čem je problém. Ale to bude tím, že jsem závadu hledal nejprve úplně jinde (píšu / psal jsem zrovna kód pro složitou aplikaci, takže než jsem se přes všechny její vrstvy dopracoval k jádru ... :o).

Oč jde?

Představte si, že máte na scéně Flash animace umístěnou instanci nebo ještě lépe více instancí movie klipu, kterým jednou za čas (v rámci resetu nastavení Flash animace) nastavujete rozměry pomocí vlastností ._width a ._height (v ActionScriptu samozřejmě). Mezi jednotlivými resety těmi instacemi po scéně různě pohybujete a někdy je i otáčíte pomocí vlastnosti ._rotation (opět v ActionScriptu). Čas od času se stane, že po resetu jsou některé instance menší, než by měly.

Možná už tušíte, kde je problém. Ano, chyba se projevuje tehdy, když je natočení "postižené" instance před resetem jiné než 0° nebo 180°.

Příčina takového divného chování je potom v pořadi, v jakém při reset nastavujete jednotlivým instancím rozměry (._width, ._height) a natočení (._rotation). Pro zjednodušení si představte, že instance jsou obdélníkové a při ._rotation=0° mají hrany rovnoběžné se scénou. Pokud budou instance před resetu natočené jinak, a reset provedete v pořadí:

instance_mc._width = sirka;
instance_mc._height = vyska;
instance_mc._rotation = 0;

bude jejich velikost (šířka a výška) nastavená tak, aby se natočené vešly do ohraničujícího obdélníku o velikosti sirka x vyska, taže po nasledném srovnání budou menší a zdeformované.

Někdy je taková věc účelná, ale pokud chcete, aby rozměry v základní poloze "seděly", musíte instance nedříve otočit do základní polohy a pak teprve nastavovat rozměry:

instance_mc._rotation = 0;
instance_mc._width = sirka;
instance_mc._height = vyska;

Pravda. Pokud rozměry instancí během pohybování s nimi neměníte a pouze je otáčíte (jak tomu bylo v mém případě), je opětovné nastavování rozměrů vlastně zbytečné (když já se ale držím zásady, že když reset, tak všeho :o). Přesto tak musíte postupovat vlastně kdykoli, kdy rozměry instance na scéně měníte a instance je v době změny rozměrů natočená jinak, než na 0° nebo 180°:

uhelNatoceni = instance_mc._rotation;
instance_mc._rotation = 0;
instance_mc._width = sirka;
instance_mc._height = vyska;
instance_mc._rotation = uhelNatoceni;

resp.:

instance_mc._rotation = 0;
instance_mc._width = sirka;
instance_mc._height = vyska;
instance_mc._rotation = novyUhelNatoceni;

Jak říkám, je to banalita :o)