Nareszcie JEST OFICJALNY FLASH z 3D!
 Oceń wpis
   

To niesamowite! Na stronie Adobe pojawiła się najnowsza edycja flasha - z numerem 11! Nie jakaś tam "beta", "rc", czy temu podobne - prawdziwy, najprawdziwszy OFICJALNY FLASH Z 3D.

Że rewolucja się zaczęła możecie stwierdzić zerkając na stronę: www.nissan-stagejuk3d.com gdzie możecie zobaczyć jak sprawuje się strona z dodatkami 3D.

Jeżeli Wam mało, to... zobaczcie zapowiedź silnika Unreala 3 w przeglądarce, odpaloną jako FLASH!

//EDIT: Zapomniałbym: nowego flasha mozecie pobrać standardowo ze strony aktualizacji Adobe.

Komentarze (1)
BitmapData.getPixel jest BE!
 Oceń wpis
   

Dzisiaj miał być kolejny odcinek cyklu "piszemy grę". Nie będzie. Możecie za to podziękować silnikowi renderującemu flasha eli edytorowi, eli metodzie getPixel...

Oczywiście można by było narzekać, że to z winy reklamacji nie ma wpisu - ale reklamacje sprawa ludzka - sprawdzić raport, rozpatrzyć, załatwić (i ewentualnie wystawić rachunek - naprawdę, zacznę wystawiać!). I tyle.

Zazwyczaj potrzebuję na załatwienie reklamacji maks 48 godzin - z czego na samo szukanie usterki góra 2-3 godziny - tym razem to ja byłem pragnienie, a flash sprite...

Jest taka ładna metoda klasy BitmapData, którą wszyscy "bitmapowcy" (tzn. pracujący we flashu z bitmapami) zapewne znają - getPixel(bmpX,bmpY); - ja używa jej do pathfindingu.

W tym reklamowanym przypadku, oprócz tła "reagującego" na bieżąco rysowało się sześć ścieżek ruchu:   0x0000ff, 0xffff00,  0xff0000, 0x00ff00, 0x00FF0F, 0xff00ff. No i za Chiny Ludowe nie chciała działać ścieżka 0x00FF0F (nie wykrywało jej) - rwałem już sobie włosy z głowy, czy to nie wina algorytmu przypisującego obiekt do ścieżki - ale nie! Obiekty biegały sobie jak im klasa sterująca nakazała.

W końcu poszedłem po rozum do głowy - tą samą metodą, którą używałem w klasie wykrywającej ścieżki, potraktowałem pierwszą linię bitmapy celu, sprawdzając, jakie kolejno kolory się ukażą:

  var clr:Number = 0;
  var c:Number;
  //
  for (var i:Number = 0; i < hitmap.getBitmapData().width; i++)
  {
   c = hitmap.getBitmapData().getPixel(i, 0);
   if (clr != c)
   {
    trace(c.toString(16));
    clr = c;
   }
  }

i oto wynik:

ffffff
ffff
0
ff
0
ffff00
0
ff0000
0
ffff
0
 ff00
0
FF0A
0
ff00ff
0
ffff
ffffff

AAAaaaAAaaaAAaaa i wiele  jeden-JEDEN-jeden (z shiftem) - widzicie zaznaczoną wartość ? Niech ktoś mi wytłumaczy, dlaczego jest tam A, a nie F ??? W edytorze wspomniana bitmapa ma wartość składowej niebieskiego 0x0F, a nie 0x0A.

Flashu, w którym momencie kłamiesz ?

Komentarze (0)
[as... ?] Zaokrąglij dziesiętnie
 Oceń wpis
   

Przeglądając przepastne otchłanie moich starych projektów znalazłem pewną metodę, która w bardzo prosty (?) sposób rozwiązywała problem sumowania błędów zaokrąglenia dziesiętnego (czyli sytuacji, kiedy 1+1 nie jest równe 2, a 1.999999999999999999999999999999 )//level - ilość miejsc po przecinku, up: 0 - w dół, 1 - w górę
function Zaokr10 (num, level, up)
{
 var log_num, pow_num, znak = 1;
 if (num < 0)
 {
  znak = -1;
  num = -num;
 }
 log_num = int (log10 (num));
 pow_num = Math.pow (10, log_num - level + 1);
 if (up == 0)
 {
  num = int (num / pow_num) * pow_num;
 }
 else
 {
  num = Math.ceil (num / pow_num) * pow_num;
 }
 return (znak * num);
}
niektóre konstrukcje są tak proste, że aż uniwersalne - ta akurat... pochodzi z projektu napisanego pod flash5, z pakiektu (tak! Z PAKIETU!!!) wspomagającego przeliczanie... 3D.

Nic dodać, nic ująć -może tylko to, że ten projekt obecnie liczy sobie... sześć czy siedem lat.

Komentarze (0)
Repeater: Interfejs - uniform, czyli garniturek
 Oceń wpis
   

Ten wpis może zawierać treści niewłaściwe dla osób niepełnoletnich... :D

Czasami ludzie pytają się mnie (podobnie jak ja kiedyś) po co ten interfejs ? No bo czytając definicję interfejsu niewiele moż na zrozumieć - jakaś klasa abstrakcyjna ? Implementacja ?

Przybliżając "na chłopski rozum" rolę interfejsu porównałbym go do kabelka z dwoma zestandaryzowanymi wtyczkami (czyli wszyscy wiedzą, co jest na jakim bolcu) - męską i żeńską, które łączą "jakieśtam" urządzenia - ale: za pomocą zestandaryzowanego protokołu. Po drugiej stronie kabelka może być cokolwiek - na kabelku może być cokolwiek, ważne jest, żeby wejscie (wtyczka żeńska :D ), była w stanie poprawnie odbierać to, co jest na wyjściu (gniazdo męskie :D ).

Mi akurat skojarzył się on z "garniturkiem" - jak uniformem: nieważne, co jest wewnątrz, kto to produkuje, ważne by na końcu wyglądało jak marynarka, spodnie, koszula i krawacik :).

A oto garniturek dla Repeatera:

interface flashfactory.pl.maw.interfaces.IRepeater {
          //
          function enable ():Boolean;
          //return false is Repeater was earlier enabled
          function disable ():Boolean;
          //return false if Repeater was earlier disabled
          function removeListener (obj:Object):Void;
          //deprecated: only for use with older projects (with Timer2.0)
          function addListener (obj:Object):Void;
          //deprecated: only for use with older projects (with Timer2.0)
          //
          function repeat (fn:Function, interval:Number, times:Number, group_id:String, as_first:Boolean):String;
          //main method: fn - funtion to repeat, interval - pause time, times - number of executions
          //repeat (fn:Function [, interval:Number [,times:Number [, group_id:String]]]):String
          //returns id of position of method
          function hold (id:String):Boolean;
          //hold ([id:String]):Boolean
          //returns true, if Repeater|member was earlier enabled
          function restore (id:String):Boolean;
          //restore ([id:String]):Boolean
          //returns true, if class Repeater|member was earlier disabled
          function reset (id:String):Void;
          //reset ([id:String]):Void
          function remove (id:String):Boolean;
          //remove ([id:String]):Boolean
          //returns false if no id found in methods table and cannot be removed
          function removeFromGroup (id:String, group_id:String):Boolean;
          //removeFromGroup (id:String[, group_id:String]):Boolean
          //returns false if group not exist or id not exist in group
          //remove id from current group or all groups
          function removeGroup (group_id:String):Boolean;
          //returns true if group was existing and removed
          function addToGroup (id:String, group_id:String, as_first:Boolean):Boolean;
          //returns false if id is incorrect, if group not exist, will be created
          //EventDispatcher methods
          function addEventListener ():Void;
          function removeEventListener ():Void;
          function dispatchEvent ():Void;
}
PS. Horrorek będzie kontynuowany... :]

Komentarze (0)
Repeater: lysta lysta i... I-lysta
 Oceń wpis
   

Po ostatnich zmianach lista metod publicznych wygląda  tak (żeby się nie powtarzać po każdej metodzie krótkie objaśnienie):

  •  enable ():Boolean
    enable zwróca wartość zmiany - a więc, jeżeli Repeater był załączony zwróci false, jeżeli był wyłączony, a został załączony to zwróci true, identycznie jak disable() zwróci informację, czy czasem repeater nie był wyłączony wcześniej:
  •  disable ():Boolean
    dodatkowo klasę Repeater wyposażę (podobnie jak Timer) w gettera i settera stanu enabled
  •  removeListener (obj:Object):Void
    (nic nie zwraca)
  •  addListener (obj:Object)
    (dwie powyższe metody zostawiam w takim stanie, jak są w Timerze)
  •  repeat (fn:Function [, interval:Number [,times:Number [, group_id:String]]]):String
    ta metoda jest sercem klasy - zwracana wartość typu string to unikatowy id przypisany naszej metodzie - uwaga! jest on różny od wpisanej nazwy grupy
  • hold ([id:String]):Boolean
    pan i władca klasy :) - zwraca info, czy Repeater był wyłączony przed zatrzymaniem
  • restore ([id:String]):Boolean
    druga metoda władcza :) - zwraca info, czy Repeater był zatrzymany przed przywróceniem
  •  reset ([id:String]):Void
    zeruje wszystkie liczniki ustawiając czas następnego wykonania metod na równy interwałowi
  • remove ([id:String]):Void 
    usuwa wszystkie powtarzane metody lub wybraną 
  • removeGroup(group_id:String):Boolean
    usuwa grupę, zwraca true jeżeli grupa istniała
  • removeFromGroup (id:String[, group_id:String]):Boolean
    usuwa metodę z grupy lub ze wszystkich grup
  • addToGroup (id:String, group_id:String):Void
    dodaje metodę do grupy - zwraca false, jeżeli metoda już w grupie wcześniej istniała, jeżeli grupa nie istnieje, założy ją

i na koniec niespodzianka - klasę wyposażę w możliwość rozgłaszania:

  • addEventListener():Void
    dodaje obiekt nasłuchujący
  • removeEventListener():Void
    usuwa obiekt nasłuchujący z listy słuchaczy
  •  dispatchEvent():Void
    "rozsiewa" event(y)

W przypadku metody repeate podanie interval, times i group nie jest obowiązkowe - jeżeli ich nie podamy metoda zadziała tak, jak byśmy używali oEFa, podanie interval (ms) spowolni wykonywanie tego "oEFa" (pod warunkiem, że interval będzie większy od czasu trwania jednej klatki - w przypadku 31 FPS będzie to więcej niż 33 ms), podanie times powoduje ograniczenie ilości wywołań metody do oznaczonej, a nazwa grupy group_id pozwoli na przyporządkowanie metody do określonej sekcji - dzięki temu rozwiąże się problem "dziedziczenia po Timerze" (pozwoli to na użycie "starej" składni) - i usuwa wcześniejsze ograniczenie Timera: jeden obiekt - jedna metoda.

A kto jeszcze nie załapał tytułu i komu jeszcze się nie nasunęło, z czym ta lista się kojarzy, temu mówię: właśnie przygotowałem podwaliny pod interfejs :)

Komentarze (0)
Dziwny problem z BitmapData
 Oceń wpis
   

Chyba z godzinę siedziałem z rozdziawioną szczęką patrząc na błąd, który mi się pojawił w outpucie:

**Error** [TOP SECRET]/_GAMESWORKSHOP/_ROZPOCZETE/[TOP SECRET]/gameloader.as: Line 10: The class or interface 'flash.display.BitmapData' could not be loaded.

Było to już po usunięciu wszystkich plików ASO, dwukrotnym przekompilowaniu i usunięciu plików *.swd.... mój mózg ciężko trybił próbując przerobić ten błąd na postać zrozumiałą dla logiki. Jak to czegoś, co jeszcze dwa dni temu było, teraz nagle nie ma ?

Idąc za wskazaniem mojego małego rozumku, po skasowaniu ASO(ó)w i SWD(f)ów pierw postąpiłem zgodnie z instrukcją w helpie - zamiast korzystać z polecenia import, usunąłem je i dopisałem pełną ścieżkę klasy do definiowanej zmiennej:

var bd:flash.display.BitmapData = ...

i ... **Error** znowu :|

idąc tropem wskazanym przez kompilator, tym razem przeniosłem importa z inkludowanego pliku do tajmlajna:

import flash.display.BitmapData;
#include "gameloader.as";

kompilator wyraźnie ze złośliwą radością wypluł, że tym razem błąd znajduje się w pliku fla :]

Cóż poradzić ? Założyłem na głowę moją sherlockowo-holmesową czapeczkę, wsadziłem w bok ust ustnik fajki i wyruszyłem na poszukiwanie łotra.

Zaglądnąłem do $(LocalData)/Classes\flash\display\BitmapData - BitmapData.as siedzi, gdzie miał siedzieć! Rzekł bym nawet: troszeczkę struchlały, bo musiałem nieźle wyglądać, jak tak przekrwionym okiem łypałem z góry na niego otwierając jego folder.

No był. I nie da się ukryć, nie zamierzał zwiać. Ba! nie ruszył się nawet, gdy zwiedziony myślą, że "może skasowanie go coś tu pomoże" mój palec zaczął ślimaczą wędrówkę w kierunku "[DELETE]". Na szczęście zdrowy rozsądek wygrał z chęcią karania niewinnych. A więc winny jest niewinny! Niech to!

Zmęłłęm swoją sherlockowo-holmesową czapeczkę i rzuciłem w kąt. Cóż ze mnie za detektyw ? :\

No ale nie jestem z tych, co się poddają po wykorzystaniu wszystkich kół ratunkowych przy pierwszym tysiącu. Milion (groszy) czeka! Ruszyłem w dalszy bój z niesfornym kodem.

Moje ciężko przesuwające się trybiki zaiskrzyły. Przypomniało mi się, że dziad jeden przy wklejeniu includa do klatki we flaku pytał mnie, czy chcę zmienić format zapisanego pliku i że po zmianie nie będzie on już odczytywalny w starszych wersjach flasha...

Wskoczyłem jak rozjuszony byk we flaka: "Gdzie jesteś Zbóju???" W pierwszej klatce go nie było. Ani w ostatniej. Popatrzyłem do zakładki Flash w Publish Settings. Action script dwa ? Dwa, no a co niby ? Stój! Ledwo się wyrobiłem przed kliknięciem w "Cancel". Mam Cię! Ha!

Mój mózg chwilę przed zamknięciem kątem oka zarejestrował, że coś mu tu nie pasuje w publikowanej wersji... FLASHA - TU CIĘ MAM!

Dwa dni temu wysyłałem Matowi plik, żeby zrobił instrukcję. Trochę byłem wtedy ciężko myślący... widocznie "przy okazji" zamiast wyeksportować do starszej wersji flasha plik z instrukcją, zapisałem w starszej wersji tego flaka. No a flash, jak to flash - "Piekło jest dobrymi chęciami wybrukowane" - zmienił automatycznie wersję publikacji na zgodną z wersją pliku. Teraz trzeba to było odkręcać. I zmienić wersję na nowszą.

Jaki z tego wniosek ? Jeżeli czegoś nie ma, a jest - usuń nie tylko pliki ASO i SWD, sprawdź nie tylko wersję Actionscript, ale także inne wersje pozycje.

Dlaczego to wyszło teraz ? Ano, zaremowanego kawałka i Salomon nie skompiluje, więc jak odremowałem, to draństwo wyszło...

...a poza tym to miłej niedzieli życzę. Bez kodowania. Exit.

Komentarze (1)
[as] Było spowalnianie, to teraz przyspieszamy
 Oceń wpis
   

W jednym z poprzednich wpisów opisałem jak spowalniać flasha kosztem procesora, teraz pora napisać jak przyspieszyć flasha kosztem procesora.

Jak zapewne wielu z czytających wie, prędkość odtwarzania uzależniona jest od FPS (frames per second), który ustawiamy w edytorze. Ci, co robią projekty pod as3 mają już troszeczkę lepiej, ponieważ mogą tego FPS-a regulować podczas pracy.

Czy my możemy wpłynąć na FPSa podczas wykonywania aplikacji ? W dowolnej wersji, nie tylko CS3/as3 ? Tak, w poprzednim akapicie to napisałem. No a jeżeli klip już jest skompilowany ?

Tutaj z pomocą przychodzi jedna z metod, które Adobe oznacza jako "deprecated". Jest to loadMovieNum - a więc załadowanie swfa na inny level. Czym się różni depth od levelu nie będę rozpisywał, w każdym razie kiedyś "depth" było słowem mitycznym, a jedyne, co szło zrobić, to załadować klipy na kolejne levele. No i nie szło ich zamieniać miejscami.

W naszym przypadku "ułomności leveli" nie są przeszkodą.

Co z tym ładowaniem ? Jeżeli w naszym klipie wywołamy loadMovieNum("nazwaklipu.swf",1); ładowanie na wyższy level, jego _root będzie oznaczał jego _level - w tym przypadku _level1 (jak zapewne pamiętacie, standardowym levelem jest _level0, co możecie w prosty sposób sprawdzić, odpalając klip w edytorze po wpisaniu w pierwszej klatce trace(_level0 == _root); )

Ok, dość już gadania. Teraz jeszcze pytanie: jak bardzo przyspieszy się odtwarzanie klipa ? Tak bardzo, jak pozwoli na to procesor. Biorąc pod uwagę, że player flasha jest stosunkowo wolny, ustawmy też na starcie _quality = "medium"; co pozwoli odzyskać część czasu z renderowania.

No i jeszcze jedno: żeby nie odpalać tysiąc razy flasha (czasami tego "wiecznego triala", co się u niektórych zdarza) dodajmy prosty skrypcik, który będzie zczytywał nazwę ładowanego klipu z nazwy swojej własnej:

var u:String = unescape(this._url).split("playfaster=")[1].split(".swf")[0];
//
trace(this._url.split("playfaster="));
if (u!="") {
 trace(u);

 loadMovieNum(u+".swf",1);
} else {
 trace("no file");

}
Stage.scaleMode = "noscale";
Stage.align = "TL";
_quality = "medium";

Ot, po prostu po nazwie "playfaster", po znaku równości wpisujemy nazwę pliku, który chcemy załadować. Bez rozszerzenia .swf. I to wszystko.

Niestety, ze względu na wysoki poziom zabezpieczeń playera, podany sposób działa tylko na pliki lokalne (z tej samej lokalizacji).

Pomyślcie, że cała ta sztuczka powstała przez to, że opisywane przeze mnie Age of War było na poziomie "hard" za wolne... za łatwe... Tak, jest poziom trudniejszy - "impossible" - ale są tam imho bardzo źle dobrane wskaźniki siły - udało mi się dojść do współczesności i zawsze jakoś forsy braknie na kolejnych komandosów (taa... gospodarka niedoboru... prawie jak w życiu...).

W załączniku: PlayFaster + Age Of War (4,2 MB). Polecam zagrać pierw z przyspieszeniem, a potem bez przyspieszenia... widać różnicę, co ?

Komentarze (1)
Odliczamy w dół. Plus liczba ze stałą ilością miejsc (upd)
 Oceń wpis
   

Naprawdę, chyba prościej się zrobić nie da.

1. Minutnik

Jeżeli interesuje nas czas, który będzie za 5 minut to znaczy, że do obecnego czasu musimy pięć minut dodać. Proste.

Obecny czas w Actionscript zwróci nam new Date() bez parametrów.

Czas za 5 minut ? new Date () + new Date(0,0,0,0,5,0,0) Jak to przechowywać ? Najprościej w postaci milisekundowej, a więc Date.valueOf() się kłania.

Teraz wystarczy od otrzymanej na początku końcowej daty (ironia, co ? koniec na początku - żeby programy tak się pisały :D ) odjąć bieżącą, żeby otrzymać różnicę wskazującą, czy jeszcze jesteśmy w naszej pięciominutówce, czy też już jest "po ptokach".

No i omawiany skrypcik:

 //5 minut
var endtime = new Date ().valueOf () + new Date (0, 0, 0, 0, 5, 0, 0).valueOf ();
//
trace (new Date ().valueOf ());
//
function multiPosNumber (nr:Number, pos:Number):String {
 //
 var n:String = nr.toString ();
 var o:String = "";
 //
 for (var i = 0; i < (pos - n.length); i++)
 {
  o += "0";
 }
 return o + n;
}
function showCountDown () {
 //
 var d:Date = new Date (endtime - new Date ().valueOf ());
 var ss:Number = d.getSeconds ();
 var mm:Number = d.getMinutes ();
 var hh:Number = d.getHours ();
 //
 trace (([multiPosNumber (hh, 2), multiPosNumber (mm, 2), multiPosNumber (ss, 2)]).join (":"));

if (!hh && !mm && !ss) {

trace ("czas się skończył!!!");
delete this.onEnterframe;

}
}
//
this.onEnterframe = showCountDown;

2. Liczba stałopozycyjna

Dodatkowo jeszcze przemycił się nam drugi skrypcik: multiPosNumber - metoda dodająca do naszej wartości zadaną liczbę zer - można oczywiście ją jeszcze usprawnić, by była to dowolna liczba (nie tylko zero), a nawet "ogonek" - np. " zł". No ale to już możecie zrobić we własnym zakresie.

function multiPosNumber (nr:Number, pos:Number):String {
 //
 var n:String = nr.toString ();
 var o:String = "";
 //
 for (var i = 0; i < (pos - n.length); i++)
 {
  o += "0";
 }
 return o + n;
}

Wszystko w "formie tajmlajnowej". Z przerzuceniem do klas nie powinno być żadnego problemu.

Miłego używania.

//EDIT: Liczba stałopozycyjna to pojęcie z binarki (czyli stringi "001010" itp.), ale nie widzę problemu w tym, by metoda działająca na zerojedynkowym zakresie działała też w innym.

Ponieważ i parseInt, i Number.toString(radix) zwraca typ String, a u mnie potrzebny jest typ Number, musimy przed przekazaniem do metody zrobić proste rzutowanie na Number - czyli Number(liczba.toString(2)); zwróci nam np. 1011001, a nie "1011001" - z powrotem znowu otrzymamy String. Co chyba widać ?

Komentarze (0)
StageResizer: Nowa Funkcjonalność! (update pakietu)
 Oceń wpis
   

Nowa Funkcjonalność czyli ObjectResizer! Lub prościej: skalowalne okienka.

Zastanawiałem się ostatnio, co by było, gdyby StageResizer podpiąć pod "różne Stage'e" - np. jeden miał by 320x200, drugi 520x520 itd. itp... wtedy każdy obiekt miałby własną przestrzeń!

A co szkodzi na przeszkodzie ? Założenie. Założeniem tym jest, że StageResizer musi być unikatem.

No ale w prosty sposób za pomocą dziedziczenia idzie to zmienić - wystarczy napisać nowy konstruktor - z założenia "stary" (ten ze StageResizera) powinien się nie wykonać. No niestety nie ma tak dobrze - flash przy dziedziczeniu ignoruje parametr "private" konstruktora. Trzeba było w konstruktorze Stageresizera dodać "małego ifa", który powodował odrzucenie treści (zawartości) konstruktora, jeżeli zmienna myInstance była już zdefiniowana.

Druga przeszkoda: klasa opiekująca się obiektami  - StageResizerListener - odwoływała się zawsze do klasy StageResizer - stamtąd brała wartości. Znów trzeba było dokonać małej modyfikacji. Dzięki tej modyfikacji budowa ObjectResizera to dosłownie parę linijek. Nawiasem mówiąc w większości skopiowanych ze StageResizera.

Idąc za ciosem, korzystając z dobrodziejstwa dziedziczenia (co poniektórzy: nie śmiać się z literówek :D) wykonałem te parę drobnych zmian, które w działaniu możecie zobaczyć tutaj:

SWF: Przykład działania ObjectResizera (chwyć za szary kwadracik w lewym dolnym rogu zdjęcia, zdjęcia możesz przestawiać)

za pomocą takiego kodu (plus oczywiście wszystkie te rzeczy z pakietu stageresizerPackage) można dokonać tych cudów:

import stageresizerPackage.*;
import system.Delegate;
//
var stageresizer:StageResizer = StageResizer.getInstance ();
//
objectresizer.addListener (kolo, "TL").setScaleMode ("ShowAll");
//
function windowPress (MC:MovieClip) {
 MC.swapDepths (this.getNextHighestDepth ());
 MC.startDrag (false, 0, 0, Stage.width - MC.movingMC._width, Stage.height - MC.movingMC._height);
}
function windowRelease (MC:MovieClip) {
 stopDrag ();
}
function resizePress (MC:MovieClip) {
 MC.swapDepths (this.getNextHighestDepth ());
 MC.resizingMC.startDrag (true);
 MC.onmousemove = Delegate.create (this, setResizeValues, MC.resizingMC, MC.resizer);
}
function resizeRelease (MC:MovieClip) {
 stopDrag ();
 delete MC.onmousemove;
}
function setResizeValues (MC:MovieClip, resizer:ObjectResizer) {
 //
 resizer.setAreaSize (MC._x + 0.5 * MC._width, MC._y + 0.5 * MC._height);
 resizer.onresize ();
}
function makeMovable_n_Resizable (MC:MovieClip) {
 //
 MC.resizer = new ObjectResizer (MC.resizingMC._x + 0.5 * MC.resizingMC._width, MC.resizingMC._y + 0.5 * MC.resizingMC._height);
 MC.resizer.addListener (MC.movingMC, "TL").setScaleMode ("exactfit");
 MC.movingMC.onPress = Delegate.create (this, windowPress, MC);
 MC.movingMC.onRelease = MC.movingMC.onReleaseOutside = Delegate.create (this, windowRelease, MC);
 MC.resizingMC.onPress = Delegate.create (this, resizePress, MC);
 MC.resizingMC.onRelease = window1.movingMC.onReleaseOutside = Delegate.create (this, resizeRelease, MC);
}
//
makeMovable_n_Resizable (window1);
makeMovable_n_Resizable (window2);
makeMovable_n_Resizable (window3);
makeMovable_n_Resizable (window4);
makeMovable_n_Resizable (window5);
//
window1.gfxMC.gotoAndStop (1);
window2.gfxMC.gotoAndStop (2);
window3.gfxMC.gotoAndStop (3);
window4.gfxMC.gotoAndStop (4);
window4.setMask (window4.movingMC);
window5.gfxMC.gotoAndStop (5);
//
window1.resizer.addListener (window1.gfxMC, "TL").setScaleMode ("exactFit");
window2.resizer.addListener (window2.gfxMC, "CC").setScaleMode ("ShowAll");
window3.resizer.addListener (window3.gfxMC, "CL").setScaleMode ("width");
window4.resizer.addListener (window4.gfxMC, "CC").setScaleMode ("NoBorder");
window5.resizer.addListener (window5.gfxMC, "BR");
//
window1.resizer.onresize ();
window2.resizer.onresize ();
window3.resizer.onresize ();
window4.resizer.onresize ();
window5.resizer.onresize ();

Najważniejsze rzeczy - czyli ile linijek kodu trzeba, by "ruszyć" jeden obiekt - zaznaczyłem na zielono.

Jak to działa ? Praktycznie w identyczny sposób, jak StageResizer - pokażę na prostszym przykładzie:

var max_x:Number = MC1._width;
var max_y:Number = MC1._height;
var MC1_objectresizer:ObjectResizer = new ObjectResizer(max_x, max_y);
MC1_objectresizer.addListener(MC1.childMC,"TL").setScaleMode("exactFit");
MC1_objectresizer.onresize();

danemu klipowi przyporządkowujemy odpowiedni resizer, dopisujemy (addListener) do resizera potomka (klip podrzędny do klipu, w którym stworzyliśmy instancję objectresizera) i powiadamiamy go o zmianie parametrów onresize() - to jest konieczne, by dodany potomek "wysłuchał co ma zrobić".

A w przykładach moich mini okienek - wystarczy zmieniać (np. co pewien okres czasu lub co ruch myszy - jak w przykładzie) wartości maksymalne poprzez MC1_objectresizer.setAreaSize(max_x, max_y); i następnie wywoływać onresize(), by uzyskać efekt jak w przykładzie.

...no i oczywiście poprawiło się przy okazji parę bugów :)

Pakiet można pobrać stąd: stageresizerPackage.zip (zip, 113kB)

//EDIT: Ops, zapomniałem zaktualizować pakiet na serwerze ^^ - spróbuję zoptymalizować przykłady, żeby tyle nie ważyły...

//EDIT2: Zoptymalizowane ze 794kB do 121kB 113kB

//EDIT3: Dragowanie (pingwiny) poprawione

Komentarze (1)
StageResizer:Finał - StageResizerPackage v2.0
 Oceń wpis
   

O projekcie: projekt powstał na podstawie "szkicu" napisanego w as1 (z użyciem globali i takich tam innych mniej ważnych pierdół); pierwotnym jego celem (wersji 2) było wypróbowanie i konkretne zastosowanie mojej wiedzy nt. programowania obiektowego. Jak wyszło - sami widzicie.

Zawartość Pakietu

Na pakiet składają się dwie klasy główne: stageresizerPackage.StageResizer i stageresizerPackage.StageResizerListener, trzy klasy pomocnicze definiujące postać obiektów alignu, offsetu i skali (*ModesArrayItem.as)
oraz cztery pakiety podrzędne (foldery): interfaces czyli "wzorce klas", align czyli metody właściwe dla wyrównywania obiektów względem stage'u, offset czyli metody przesunięć zależne od wybranych metod wyrównania, scale czyli metody właściwe dla wybranego skalowania obiektu względem stage'u.

Uwaga: klasa StageResizer zmienia samodzielnie align Stage'u na "TL" oraz wyłącza ustawione scaleMode na "noScale".

Metody publiczne klasy StageResizer

W poniższych opisach metod tych klas będę omijał ścieżkę pakietu i odwoływał się do samych nazw. Tak samo w klasach podrzędnych.

1. Metody statyczne - pozwalają rozszerzyć klasy ustawień o dodatkowe funkcjonalności

addAlignMode (mode:String, alignXClass:IStageResizer_AlignX, alignYClass:IStageResizer_AlignY) dopisuje do istniejącej listy wyrównań nowy sposób wyrównania, przypisane klasy muszą być odpowiedniego typu interfejsu

addOffsetMode (mode:String, offsetXClass:IStageResizer_OffsetX, offsetYClass:IStageResizer_OffsetY) jak wyżej, odpowiada za dopisanie nowego offsetu

addScaleMode (mode:String, scaleClass:IStageResizer_Scale) odpowiada za dopisanie nowego sposobu skalowania - uwaga: metoda przyjmuje tylko jedną klasę działającą po X i Y równocześnie (takie rozwiązanie było konieczne ze względu np. na skalowanie proporcjonalne)

mapAlignMode (mode:String, targetmode:String):Boolean pozwala zamapować (dodać nową nazwę wywołania sposobu) mode w istniejący sposób wyrównania, zwraca false gdy mapowanie było nie udane

mapOffsetMode (mode:String, targetmode:String):Boolean jak wyżej, dla offsetu
mapScaleMode (mode:String, targetmode:String):Boolean jak wyżej, dla skalowania

2. SINGLETON

 StageResizer jest klasą typu singleton (unikat), a więc jej konstruktor jest prywatny, do wywołania klasy służy metoda getInstance() zwraca: (i tworzy) referencję do klasy

3. Metody publiczne dostępne po wywołaniu singletona

addListener (MC:MovieClip, onstagepos:String [, scalemode:String]):StageResizerListener - dodaje obiekt do listy słuchaczy klasy SR, zwraca referencję do obiektu nasłuchującego (uwaga!!! StageResizerListener != MovieClip); w przypadku dodania słuchacza tylko z jednym parametrem (onstagepos czyli align) klasa SR próbuje ten parametr przypisać pozostałym ustawieniom - a więc i offsetowi, i skalowaniu.

removeListener (MC:MovieClip) - usuwa obiekt z listy słuchaczy

addEventListener (MC:Object) - dodaje listenera do słuchaczy klasy Stage (uwaga!!! to jest tylko przekazanie, obiekt dodany tą metodą _nie_będzie_ miał stworzonego obiektu nasłuchującego w klasie SR!)

removeEventListener (MC:Object) - usuwa obiekt z listy słuchaczy klasy Stage (uwaga: jeżeli użyłeś wcześniej addListener, to obiekt pozostanie dalej w liście słuchaczy klasy SR)

dwie powyższe metody zostały dodane po to, by SR obsługiwała "kompleksowo" ułożenie obiektów na Stage'u, wraz z rozgłaszaniem.

setStageAreaAtStart (startwidth:Number, startheight:Number) ta motoda jest wywoływana w konstruktorze, na wartościach zdefiniowanych tą metodą opiera się przeliczanie wielkości w podstawowych metodach skalowania; domyślne wartości to Stage.width i Stage.height, lecz jeżeli potrzebujesz, by Twoje obiekty widziały "mniejszą rozdzielczość" użyj np.: setStageAreaAtStart(1024, 768);

setListenerAlignMode (MC:MovieClip, mode:String):Boolean
setListenerOffsetMode (MC:MovieClip, mode:String):Boolean
setListenerScaleMode (MC:MovieClip, mode:String):Boolean

te trzy metody powyżej pozwalają ustawić wyrównanie, offset, skalę dla danego obiektu bez odwoływania się do referencji obiektu słuchacza, zwracają false jeżeli obiekt jest nieprzypisany lub metoda nieznaleziona

setListenerOffset (MC:MovieClip, offsetX:Number, offsetY:Number) pozwala na ustawienie określonego parametrami offsetX i offsetY przesunięcia, o ile obiekt jest zarejestrowany w klasie SR

updateListener (MC:MovieClip):Boolean w przypadku zmian (np. rozwinęło się okienko, skończyła animacja) pozwala na aktualizację parametrów wyświetlania, jeżeli udana zwraca true; po wywołaniu metody wskazane jest wywołać "ręcznie" metodę onresize() klasy StageResizer

setListenerAutoOffset (MC:MovieClip, autooffset:Boolean) ustawia/przywraca automatyczny offset (uwaga: ta metoda może działać niepoprawnie)

onresize () przekazuje wszystkim obiektom nasłuchującym zajście zdarzenia Stage.onresize

notifyListener (listener) działa podobnie jak dla updateListener z tym, że odwołuje się do konkretnego obiektu nasłuchującego

4. Metody publiczne, których nie należy używać, jednak przydające się przy własnoręcznym rozszerzaniu pakietu

getAlignXMode (mode:String):IStageResizer_AlignX zwraca referencję do klasy przypisanej wyrównaniu wzdłuż osi X

getAlignYMode (mode:String):IStageResizer_AlignY jak wyżej, dla osi Y

getOffsetXMode (mode:String):IStageResizer_OffsetX zwraca referencję do klasy przypisanej przesunięciu wzdłuż osi X

getOffsetYMode (mode:String):IStageResizer_OffsetY jak wyżej, dla osi Y

getScaleMode (mode:String):IStageResizer_Scale zwraca referencję do klasy odpowiedzialnej za skalowanie

Metody publiczne klasy StageResizerListener

Metody te pozwalają na "szybsze" zmiany słuchacza. Ponieważ metoda addListener klasy StageResizer zwraca referencję do instancji klasy StageResizerListener, pozwala to na użycie bezpośrednie metod od razu po użyciu metody addListener np. stageresizer.addListener("bg","TL").scaleMode("exactfit");

1. Klasa SRL nie posiada metod statycznych

 i tyle na ten temat

2. Konstruktor publiczny: StageResizerListener(_MC:MovieClip)

 Uwaga: nie zalecam używania samodzielnego klasy StageResizerListener - i tak zostanie zainicjowana klasa SR i wg jej właściwości zostaną dobrane parametry obiektu.

3. Metody przydatne przy zarządzaniu ustawieniami

onresize () metoda wywoływana po przekazaniu zdarzenia Stage.onresize, uaktualnia właściwości obiektu

setAlignMode (mode:String):Boolean
setOffsetMode (mode:String):Boolean
setScaleMode (mode:String):Boolean

powyższe metody powodują ustawienie właściwych danemu stringowi "mode" klas ustawień

setOffset (ox:Number, oy:Number)
setOffsetX (ox:Number)
setOffsetY (oy:Number)

powyższe metody ustawiają offset odpowiednio: dla obu osi, dla oX, dla oY

4. Metody publiczne, których nie należy używać, jednak przydające się przy własnoręcznym rozszerzaniu pakietu

setAutoOffsetMode (mode:Boolean) nie obsługiwana

setAlignXBehavior (alignbehavior:IStageResizer_AlignX)
setAlignYBehavior (alignbehavior:IStageResizer_AlignY)
setOffsetXBehavior (offsetbehavior:IStageResizer_OffsetX)
setOffsetYBehavior (offsetbehavior:IStageResizer_OffsetY)
setScaleBehavior (scalebehavior:IStageResizer_Scale)

Metody "behaviorów" są odpowiedzialne za przypisane odpowiednich klas interfejsu (wzorzec strategia)

Właściwości publiczne klasy StageResizer

_actualWidth:Number
_actualHeight:Number
przechowują informacje nt. aktualnej wielkości Stage'u

_actualWidth05:Number;
_actualHeight05:Number;
jak wyżej, tylko dla środka Stage'u

_stageScaleX:Number;
_stageScaleY:Number;
przechowują wyliczoną "skalę" czyli zmianę obszaru Stage'u względem wartości podanych w metodzie setStageAreaAtStart

Właściwości publiczne klasy StageResizerListener

frozen:Boolean - wartość true powoduje opuszczenie metody onresize
alignMode:String,offsetMode:String,scaleMode:String - przechowuje łańcuch opisujący określone mode
dynamicOffset:Boolean - wartość true powoduje ominięcie przeliczania offsetu przy wywołaniu onresize
x0:Number, y0:Number, w0:Number, h0:Number, bounds:Object, s0y:Number, s0x:Number - przechowują wartości początkowe obsługiwanego klipu
offsetX:Number, offsetY:Number - przechowują "ręczne ustawienia" przesunięcia względem osi

Ze względu na bezpośrednie wywoływanie wartości przez klasę nadrzędną (SR), a także przez problemy przy delegacji (brak widoczności właściwości) poniższe metody także są publiczne, jednakże nie należy ich wykorzystywać poza sytuacjami, gdy wymaga to rozszerzanie pakietu

setXAlign:Function = function (_MC:MovieClip, oX:Number);
setYAlign:Function = function (_MC:MovieClip, oY:Number);
setScale:Function = function (_MC:MovieClip);
getXOffset:Function = function (_MC:MovieClip);
getYOffset:Function = function (_MC:MovieClip);
scaleBehavior:IStageResizer_Scale;
offsetXBehavior:IStageResizer_OffsetX;
alignXBehavior:IStageResizer_AlignX;
offsetYBehavior:IStageResizer_OffsetY;
alignYBehavior:IStageResizer_AlignY;

Tabela predefiniowanych wartości

Uwaga: ze względu na ułatwienia dla "szybkiego programowania" wszystkie stringi opisujące tryby ustawień są konwertowane na duże litery, a więc _wielkość_liter_jest_ignorowana_; litery są dobrane wg pierwszych liter nazw angielskich i w większości zgodne z parametrami Stage.align oraz Stage.scaleMode

Alignmodes

Nazwa
pozycja X
pozycja Y
mapowane
TL
lewo
góra
 
TC
środek
góra
 
TR
prawo
góra
 
LC
lewo
środek
CL, LM
C
środek
środek
CC, C%
HC
środek
brak
 
M
brak
środek
VM
M%
proporcjonalnie
środek
 
RC
prawo
środek
RM, CR
BL
lewo
dół
 
BC
środek
dół
 
BR
prawo
dół
 
T
brak
góra
 
T%
proporcjonalnie
góra
 
B
brak
dół
 
B%
proporcjonalnie
dół
 
L
lewo
brak
 
L%
lewo
proporcjonalnie
 
R
prawo
brak
 
R%
prawo
proporcjonalnie
 
V
brak
proporcjonalnie
H%, %V
H
proporcjonalnie
brak
V%, %H
R%
proporcjonalnie
proporcjonalnie
%%
*
brak
brak
*, none, noAlign

Offsetmodes

Ze względu na zgodność z trybami wyrównania, niektóre pozycje pomimo, że mają przypisane wartości, jednak nie wywołują żadnego przesunięcia; wszystkie przesunięcia są dokonywane względem środka obiektu wyliczonego na podstawie getBounds()

Nazwa
pozycja X
pozycja Y
mapowane
R
na lewo
brak
right, R%
L
na prawo
brak
left, L%
HC
wyśrodkowane
brak
 
VC
brak
wyśrodkowane
 
T
brak
poniżej
T%, top
B
brak
powyżej
B%, bottom
M
brak
środek
middle
noOffset
brak
brak
none, *, %, %H, %V, H%, V%
TL
na prawo
poniżej
 
TC
środek
poniżej
 
TR
na prawo
poniżej
 
LC
na prawo
środek
LM, CL
C
środek
środek
CC
RC
na lewo
środek
RM, CR
BL
na prawo
powyżej
 
BC
środek
powyżej
 
BR
na lewo
powyżej
 

Scalemodes

Podane metody są zbieżne z trybami skalowania klasy Stage

Nazwa
Metoda
Odpowiednik klasy Stage
mapowane
NoScale
brak skalowania
"noScale"
*, none
NoBorder
obcinanie
"noBorder"
 
Proportional
proporcjonalnie
brak
%, T%, B%, L%, R%
ShowAll
maksymalnie widoczne
"showAll"
 
ExactFit
nieproporcjonalne
"exactFit"
 
Width
dopasuj szerokość
brak
 
Height
dopasuk wysokość
brak
 
Vertical
na wysokość proporcjonalnie
brak
V%, %V
Horizontal
na szerokość proporcjonalnie
brak
H%, %H

W folderze $test znajduje się kilka przykładowych użyć projektu.

Uwagi końcowe

Do działania konieczna jest poszerzona delegata, która musi znajdować się w folderze system, jej kod to:
//****************************************************************************
//Extended Delegate - sends all parameters to the called function
//****************************************************************************
//
//
class system.Delegate {
 /**
 Creates a functions wrapper for the original function so that it runs
 in the provided context.
 @parameter obj Context in which to run the function.
 @paramater func Function to run.
 */
 //
 static function create (obj:Object, func:Function):Function {
  //
  var extraArgs:Array = arguments.slice (2);
  var f = function () {
   var target = arguments.callee.target;
   var func = arguments.callee.func;
   var args:Array = arguments.concat (extraArgs);
   return func.apply (target, args);
  };
  f.target = obj;
  f.func = func;
  return f;
 }
 //
 private function Delegate () {
 }
}
za tą delegatę możecie podziękować Semie(www.devlog.szemraj.eu) - bez niego nie udało by mi się do końca zrobić przekazywania parametrów i zwracania wyników.

Najprostsze użycie jako skalowanego tła:
Uwaga: nie odpowiadam za błędne działanie pakietu, za skutki uboczne i wpływ na inne metody jak np. efekty nadpisania flashowej klasy Delegate obecną delegatą, czy też błędne działanie z innymi managerami, nieprzetestowanymi we współpracy ze StageResizerem.

import stageresizerPackage.*;
//
var sr:StageResizer = StageResizer.getInstance();
sr.addListener(this.moje_tlo,"TL").setScaleMode("exactFit");
  No i jeszcze na koniec: dzięki Arthwoodowi zrozumiałem jak przydatne są interfejsy, Edi (Edicapo) spojrzał na mój kod krytycznym wzrokiem (przepraszam Edi, że Twoich uwag nie wykorzystałem :D), Arturowi (r2d2) za jego marudzenie "jak mi się chce", którym mnie czasami rozśmieszał do łez... no i wszystkim tym, którzy czytali wpisy o StageResizerze, choć się do tego nie przyznawali :)

A wpis ten ujrzeliście tak szybko dzięki temu, że na forum webesteem też są fajne ludziska w dziale flash ;)

Pakiet można pobrać stąd: stageresizerPackage.zip (zip, 52kB)

Komentarze (0)
Jak schackierować SWFa ?
 Oceń wpis
   

Jak niektórzy się domyślają (a inni już wiedzą) SWF ("*.swf") to rozszerzenie pliku wykonywalnego flasha, a więc APLIKACJI FLASHOWEJ - w odróżnieniu od FLA ("*.fla"), który nawet nie jest plikiem kodu źródłowego, ponieważ oprócz instrukcji Action script (które coraz częściej /OOP/ są umieszczane w zewnętrznych plikach ("*.as") zawiera "instrukcje" wyglądu stage'u, pozycje klipów i parametry poszczególnych klatek movieclipów, które nie mają odzwierciedlenia w kodzie APLIKACJI (czyli pliku *.swf). To gwoli wytłumaczenia klientom, że nawet  kupując wykonanie aplikacji flashowej od zera, nie kupują zarazem praw do pliku FLA (* o ile umowa nie stanowi inaczej), ale o tym będę jeszcze pisał w innym wpisie.

Przejdźmy do rzeczy - parę wpisów temu popełniłem jeden wpis o łamaniu swfów, gdzie rąbek tajemnicy, który uchyliłem, był... bardzo mały.

Oczywiście pytania prywatne były, a jakże! No więc jak na prywatne pytania uchyliłem prywatnie więcej rąbka tajemnicy, to nawet przez niektórych zostałem obśmiany (no a jakże, przecież "oczywiste oczywistości" gadam). Np. przy tym, że jeden ze sposobów nazwałem "image injection" - oczywiście szczegółów nie poznali, ale obraz na swoje podobieństwo zbudowali (a kto im broni ? nikt :P)

To tyle tytułem wstępu. Pora teraz uchylić taaaaakiego rąbka, czyli przybliżyć parę sposobów jak zobaczyć, co w swfie piszczy (pizga ?).

MEMORY CRAWLER

Sposób najprostszy, bo bez ingerencji w swfa - ot, po prostu wystarczy sobie załatwić skaner pamięci. Jak to działa, nie będę opisywał, bo już ktoś to zrobil za mnie. A oto pokaz (z jutjuba) przeprowadzany na jednej z opisywanych przeze mnie gier - BOXHEADS

A tu link do tego, jak to zrobić (opisują twórcy programu - jednego z wielu): how to cheat flashgames. Oczywiście nie ma akcji bez reakcji: poznaj przeciwnika

Teraz pora pobawić się tym, co przez przeglądarkę przechodzi...

HTTP PROTOCOL SCANNERS

Tu nie ma co opisywać. Są programy, które zajmują się skanowaniem portów HTTP, śledzą co wchodzi, a co wychodzi z przeglądarki - często same są pluginami do przeglądarki, jak np. Data Temper i Live HTTP Headers do FireFoxa. Są też zewnętrzne, działające na konkretnym IP. Cała sztuczka polega na tym, żeby domyślić się co, gdzie, w danym miejscu ciągu oznacza.

DOWNLOAD & MODIFY

Opuszczamy teraz przeglądarkę - no może za chwilę, bo pierw musimy ściągnąć dodatek (jak się nazywa, to nie powiem, bo sam nie pamiętam) do przeglądarki, który pozwoli nam "na żywca" skopiować swfa z przeglądarki. Jak nie mamy takiego narzędzia, to wystarczy wziąć znaleźć na dysku pliki *.swf zapisane w ciągu ostatnich 10-30 minut. Oczywiście nie jest to sposób pozyskania w 100% skuteczny, ale od biedy...

Ok, download już był, teraz pora na modify - a to już wszyscy znają - ściąga się jakiegoś swf dekompilatora i bawi nim. W najgorszym przypadku dostaniemy tylko obrazki, ale to już coś ;)

No ale na akcję jest reakcja - po to ktoś wymyślił obfuskatory, żeby zabezpieczyć swfa przed dekompilowaniem.

CODE RESEARCH

No to... gdy mamy do czynienia z obfuskatorem: zapewne obrazki wyjmiemy, ale poza tym, jedyne co dostaniemy to taką sieczkę:
var \x1 = 681;
while (\x1 = eval("\x1") + 49, eval("\x1") == 670)
{
    \x1 = eval("\x1") - 669;
    break;
}
\x1 = eval("\x1") - 683;
if (eval("\x1") == 669)
{
    \x1 = eval("\x1") + 23;
   
   
}
\x1 = eval("\x1") - 594;
if (eval("\x1") == 930)
{
To już jest przepapane - całego kodu nie ściągniemy, ale... skoro mamy już swfa na dysku, to... zapiszmy w tym samym folderze drugiego swfa, z takim kodem (test.swf to oczywiście nazwa pliku, który ściągnęliśmy na dysk):

var MCL:MovieClipLoader = new MovieClipLoader ();
MCL.loadClip ("test.swf", createEmptyMovieClip ("test", 1));
MCL.addListener (this);
this.onloadInit = function (MC) {
 for (var name in MC)
 {
  for (var val in test[name]) {
   trace(name + "." + val + " = " + test[name][val]);
  }
 }
};

i odpalmy pod kontrolą edytora, albo jakiegoś innego debuggera... Kodu raczej nie zobaczymy, ale zmienne, wartości, nazwy klas już nam wiele dadzą. Jaki z tego wniosek ?

OBFUSKATOR ZABEZPIECZA WYŁĄCZNIE PRZED DEKOMPILACJĄ! Nie przed zajrzeniem w działający kod!

SHARED OBJECTS VIEWER

SO - czyli te "flashowe cookies" o których kiedyś pisałem, też nie są tak bezpieczne, jak się wydaje. Stąd: http://solve.sourceforge.net/ można pobrać projekt, który pozwala bez problemu oglądać te "czaszczeczka" (tylko szę nie udłafcze) i edytować je - a przynajmnej wartości cyfrowe i stringi.

Poza powyższym sposobem mam jeszcze dwa inne, w których da się SO wykorzystywać. Jeden - to wszystko możemy podejrzeć ręcznie (nawet odpalić), jeżeli tylko plik SO (odpowiednio spreparowany) umieścimy w folderze odpowiadającemu SO naszego projektu. Drugi - o nim trochę powiedziałem na spotkaniu "After Party" w Katowicach. Żeby nie rzucał się w oczy: nazwałem go "Function Injection". Koniec na ten temat.

IMAGE INJECTION

I za tą nazwę zostałem obśmiany, a tak dużo można przez to uzyskać!

Wyobraźmy sobie serwis, który pozwala na upload fotek we flashu, z wykorzystaniem w aplikacji flashowej - np. jak na miniklipie ta mutowarka zdjęć. Teraz pomyślmy, że flash w ten sam sposób ładuje i pliki *.jpg, jak i inne *.swf-y. Oświeciła się lampka ?

Jeżeli zmienimy rozszerzenie pliku *.swf na *.jpg, a po stronie serwera nie będzie niczego, co by sprawdzało faktyczną postać pliku, to praktycznie furtkę do swfa już mamy otwartą. I to ONLINE!!! Łącząc teraz niektóre z opisanych sposobów w logiczną całość razem z tym sposobem, mamy władzę nie tylko nad swfem - nad transmisją, zapisem wyników, śledzeniem użytkownika, a w najgorszym przypadku... mamy możliwość zapisu własnego pliku php na serwerze!!!

Dobra, jeżeli ktoś już sprawdza bity (czyli rzeczywistą postać plików), to wie, że około 128 pierwszych bajtów jest ważne - a jeżeli by tak wypróbować, czy administrator serwera zezwolił na odpalenie skryptów php spod innych rozszerzeń ? A pole description jpg-a ?

No to macie teraz prawie 50% mojej wiedzy ;)

Acha: i nie piszcie w komentarzach, że ten a ten idzie zabezpieczyć w taki a taki sposób (chyba, że chcesz się podzielić jakimś swoim autorskim sposobem, ale to już wtedy skontaktuj się prywatnie, a nie poprzez komentarz) - bo ja wiem, że na każdy z tych sposobów da się znaleźć odpór :D - jak już napisałem na każdą akcję znajdzie się reakcja.

Komentarze (0)
StageResizerProject - upublicznienie
 Oceń wpis
   

Prawie :)

Bo zanim wyedytuję ten post i wkleję linka do projektu, to mam do Was pytanie: czy jest ktoś chętny do testowania ? Zgłosić się we wiadomy sposób ;-) - można zostawić swój mail w komentarzach i ewentualnie jakieś info o sobie (jeśli się nie znamy ofc'rz :) ) - nie będzie publikowany.

No i zanim opublikuję kod wypadało by dokumentację metod opisać... mam nadzieję, że mi w tym pomożecie...

Taka uwaga ogólna: projekt jest pisany pod standardowym pakietem projektów flasha (*.flp), klip testujący wymaga ustawienia ścieżki do klas: [...]/system i [...]/ - z tego względu, że niefortunnie zastosowałem delegatę poprzez import system.Delegate; żeby dało się ją odróżnić od tej systemowej. Możecie za nią podziękować Semie - pozwala nie tylko na przekazywanie parametrów, ale także na ich zwracanie do obiektu wywołującego.

Podstawowe metody w postaci oznaczeń tekstowych są w plikach "predefined[Align|Offset|Scale]ModesList.as", można użyć i pojedyńczych wywołań jak stageresizer.addListener(MC,"BR"); jak i stageresizer.addListener(MC,"CL").setScaleMode("width"); oraz stageresizer.addListener(MC,"CC").setOffsetMode("none");

Zresztą po co ja to piszę ? wszystko można wyczytać z poprzednich wpisów projektu :)

Komentarze (0)
Zabezpieczenia zostały złamane!
 Oceń wpis
   

Krótka notka.

Wczoraj, późną nocą, udało mi się znaleźć sposób, aby obejść kodowanie swfów obfuskatorami. Oczywiście te związane z zabezpieczaniem danych, a nie z czytelnością kodu. Choć zapewne jakby się uprzeć, to i kod dało by się "przywrócić".

Uchylę rąbka tejemnicy - sposób wejścia nazwałem... nie - za dużo bym powiedział :). To drugi sposób - skuteczniejszy, a zarazem prostszy niż skanowanie pamięci :).

I jeszcze jedno: żaden allowDomain ani secureDomain przed tym nie zabezpieczy. Ani serializacja czy też hash'owanie transmisji.

Czy gra typu "jeszcze bardziej efektywne zabezpieczanie swfa" jest warta świeczki ? Myślę, że jest. Chociażby z powodu aplikacji konkursowych, przesyłania poufnych danych itp.

Jest parę sposobów, które na bieżąco testuję pod kątem zabezpieczenia przed tego typu włamianiami. Na skanery pamięci sposób już mam. Na pozostałe... wiadomo, zawsze znajdzie się ktoś przebieglejszy...

Nie - nie zamierzam nikomu rzucać rękawicy - po prostu, poprawiam komfort psychiczny moich klientów ;-).

Komentarze (1)
Bezpieczne przesyłanie danych - jak narobić smaczka ?
 Oceń wpis
   

Ostatnio mam zagwózdkę z okazji zapytania klienta pt "czy da się bezpiecznie we flashu przekazywać dane na serwer". Jak na razie jestem w fazie prób, ale jak się wydaje, wszystko będzie powoli szło w dobrym kierunku.

Ponieważ projekt ten miałby obejmować stronę flashową, serwerową i desktopową, do tego projektu będzie potrzebna osoba dobrze poruszająca się w AIR. No i oczywiście ktoś, kto w php umie różne dobre rzeczy wyczarowywać. Zgłoszenia i oferty współpracy mile widziane.

Wracając do tematu. Dotychczas - oprócz sposobów z "(r)" w nazwie - do przesyłania danych używałem metody scramble() i unscramble() które według określonego algorytmu "haszowały" mi dane. Oczywiście problem był tego typu, że efektem działania był string.

Jak to bywa w takich sytuacjach, trzeba było pójść po rozum do głowy i od czegoś zacząć.

Ja zacząłem od tego, że spytałem się administratora serwera, czy wywołanie https można przesunąć na inny port. Okazało się, że jest taka możliwość. Następnym krokiem było sprawdzenie, czy flash sobie poradzi z takim przesyłem po htps - tak, jak najbardziej - przydał się plik crossdomain.

Po swojej stronie przerobiłem troszeczkę metodę scramble() - znaczy: napisałem ją na nowo. Tym razem zmiana polegała na tym, że dla danego obiektu została zbudowana tabela typów - scramblowany obiekt już nie wraca w postaci stringu, lecz... obiektu! Oczywiście takiego, z którego niewiele można odczytać... gdyby nie tabela typów.

Tak spreparowany obiekt nadaje się teraz do wysłania przez amfphp na serwer.

A resztę - osłońmy zasłoną milczenia...

Komentarze (3)
Skrypt dla "kroczącego wygaszania"
 Oceń wpis
   

Wyrzucam z projektu jako niepotrzebne, a by się nie zmarnowało... zostawiam na blogu.

Ten skrypt powoduje powolne "wygaszanie" (_alpha) kolejnych obiektów o nazwach group+[nr] od nr 8 do 1, ale bardzo prosto zmienić tą numerację na inną.

...wyrwane z większej całości - żeby nie było Wam za łatwo :P

 private function hideButtons (group:String, _MC:MovieClip) {
  //
  var i:Number = 8;
  var tMC:MovieClip;
  //
  do
  {
   tMC = MC[group + i];
   trace ([group, i, tMC]);
   //
   with (tMC)
   {
    if (!_visible)
    {
     i--;
     continue;
    }
    else
    {
     _alpha -= 10;
     if (_alpha <= 0)
     {
      _alpha = 100;
      _visible = false;
     }
     else
     {
      break;
     }
    }
   }
   i--;
  } while (i > 0);
  //
  if (i == 0)
  {
   timer.removeListener (_MC);
  }
 }

uwaga: potrzebuje Timer-a do inicjalizazji:

timer.addListener(_MC, 0, Delegate.create(this, hideButtons, group, _MC));

Komentarze (0)
Uwaga dla zmiennych prywatnych
 Oceń wpis
   

Zmienne prywatne - to te, których wartość ustalamy po stworzeniu nowej instancji (wywołaniu konstruktora) klasy.

O tym już było kilkakrotnie i na flabie, i na różnych blogach:

class Foo {

private var foofoo:String = String("coś");

public function foobutton (_MC:MovieClip) {

//ta metoda będzie potrzebna nam później

trace("to jest onrelease buttona "+_MC+", czy teraz jesteśmy w buttonie ? "+(_MC==this));

trace("Widzisz, że nie widzisz: " + foofoo);

}

public function Foo () {

trace("a teraz właściwość prywatna działa jak statyczna: " + foofoo);

}

}

^ tak zdefiniowana właściwość dopóki jej nie zdefiniujemy/nadpiszemy w metodach instancji, będzie funkcjonowała jak zmienna statyczna.

Ale nie o to mi teraz chodzi. Kiedyś znajomy na gg napisał mi, że w zasadzie po wyjściu z edytora (po skompilowaniu swfa) nie ma znaczenia, czy zmienne są prywatne, czy publiczne - do każdej można się odnieść bezpośrednio poprzez this.foofoo. Nieprawda. Właśnie się przekonałem, że w runtime maszyna wirtualna "pamięta", które są publiczne, a które prywatne.

Jak się przekonałem ? W prosty sposób:

//kod w klatce

import Foo;

import Delegate2;//Delegate2 pozwala na przekazywanie zmiennych - np. kod delegate z mojego bloga (choć polecam wyguglać delegate2)

var myButton:MovieClip;

var foonstance = new Foo();

myButton.onRelease = Delegate.create(foonstance, foonstance.foobutton, myButton);

myButton.onRelease();

wystarczy zmienić private na public właściwości foofoo, by jej wartość pojawiła się w output zamiast undefined.

Komentarze (0)
[as2]Singleton: czy konstruktor musi być prywatny ?
 Oceń wpis
   

Ha! Panowie FlashDeveloperzy! OOP nie kończy się tylko na standardzie ECMA :>

Tuż za rogiem maliboo głosi peany na cześć 10tego flashPlayera (żeby nie było, że "znowum przegapił" - czytaj: nie wspomniał), a ja męczę te OOP starając się wycisnąć każdą kropelkę - tfu, każdą literkę - z ust moich rozmówców.

Skończyłem właśnie rozmowę ze znajomym (nie chce się podlinkować skubany, no!), który nie jest nikim szczególnym - ot, pisze doktorat z informatyki :]. O co sprawa poszła ? Bo w końcu co osoba pracująca na całkiem innej platformie (nie wiem, czy to był .net z pythonem, czy .python przez .net - wthw ?) może powiedzieć "flashowcowi" ?

Otóż rozmawialiśmy sobie o wzorcach (czego mi jeszcze brakuje :]). Oczywiście nie można było nie wspomnieć o singletonie i o gromach, jakie zebrałem za "niezgodne ze wzorcem" zmodyfikowanie klasy, które to jak się wydaje, poleciały na mnie całkiem niesłusznie.

Ci, którzy starają się trawić OOP już w trzeciej wersji Action scriptu (as3.0) wiedzą, że nie ma tam konstruktorów prywatnych. I uważają to za ciężką przeszkodę.

Mając problem z niekończącą się pętlą przy StageResizerze pomimo zabezpieczenia singletonem nie wpadłbym na to, że takie rozwiązanie będzie podstawą "wykładu" mojego znajomego.

Otóż: programiści dzielą się na programistów różnych poziomów. Jedni zajmują się programowaniam strukturalnym, inni programowaniem poziomu aplikacji. Jego zdanie jest takie, że propgramistów aplikacji wogóle nie powinno obchodzić, czy klasa, którą będą używać, będzie singletonem, czy wielokrotną. Co oznacza, że wywołanie sinlgetona powinno się odbywać tak samo, jak wywołanie "zwykłej" klasy...

Zapewne mi nie wybaczy, jak powiem, że połowy z tego, co mówił (pisał) nie zrozumiałem, ale żeby zrozumieć ocb skoczyłem po rozum do głowy, a tam czytam:

Mający wpływ na rozwój języka Python, pracujący obecnie dla Google Alex Martelli, członek Python Software Foundation, twierdzi, że języki obiektowe tak naprawdę nie potrzebują Singletonu. Twierdzi on, że jak długo będziemy dbać o to, by współdzieliły pewien abstrakcyjny stan, tak naprawdę spełniamy warunek, (potrzebny do tego by jeśli zaistnieje taka potrzeba) ażeby ów stan był unikatowy. Kontrolowanie by liczba instancji danej klasy była najwyżej równa jeden, jest tylko jednym ze sposobów (cytat za: wikipedia, wzorzec singletonu)no i git. Mi się to też wydaje jak najbardziej logiczne.

Komentarze (0)
StageResizer - Obywatelu, w tył zwrot i jeszcze raz
 Oceń wpis
   

Zmiana kontekstu metod. Say'z'gon - jeszcze dziś w południe pisałem jak to fajnie ominąłem problem z singletonem. Pomyśleć, że na nic to było - choć wiadomo, na przyszłość w głowie conieco pozostanie (przyda się wiedzieć, jak obejść taką sytuację).

StageResizer - rzut okiem na blat...
Niestety, okazało się, że wszystko na próżno. Z mojej winy. Napisałem wczoraj, że zostawiam sobie klasy skalujące na koniec. Niewybaczalny błąd. Być może nie wystąpiłby wtedy, gdybym zajmował się tylko projektem SR, a nie brał się za niego w wolnych momentach... niestety, stało się.

Aby skalować obiekt proporcjonalnie (bo to akurat mam teraz na blacie) muszę znać jego wielkość początkową - obojętnie, czy _width i _height, czy _xscale i _yscale - muszę korzystać z wartości początkowych klipu.

Oznacza to jedno - że istnieje potrzeba przekazania większej ilości zmiennych do klasy behawioralnej, niż tylko referencja do klipu. Można to zrobić albo poprzez wyłapywanie tych zmiennych z tabeli argumentów metody, co IMHO jest wogóle nieoopowe - bo cokolwiek bym nie wyłapał, nie będę wiedział jakiego jest typu i czy jest to akurat to, co chciałem pobrać - jak również można to zrobić wywołując daną metodę w kontekście listenera, którego ta metoda dotyczy.

Taaak... oświeciła się lampka. Flashowcy mają odpowiednie metody na to - ruszyła w ruch kiedyś przeze mnie pisana delegacja.

Szkoda, że nie mogę się trzymać stricte wzorców, jak zakładałem, ale wszyscy musimy pamiętać o tym, że to nie my jesteśmy dla definicji, lecz to definicje są dla nas.

 Założenia projektu (link do wpisu).

//EDIT: I taka duża edytka: nie ma tak fajnie, jak by się chciało. Po pierwsze: nie patrzcie na moją starą delegację, jest do kitu (link usuwam). Po drugie: aby korzystając ze wzorca korzystać ze zmiennych lokalnych przy delegacie, musimy mieć je w dwóch miejscach: w klasie kontekstowej oraz w definicji klasy, której metodę będziemy delegować.

Miałem ja ci taki wzór (na skróty):

//w SRListenerze:
private var _stageresizer:StageResizer;
private var MC:MovieClip;//ok, ładnie się ustawiło
private var offsetX:Number;
var setXAlign:Function = function (MC:MovieClip, offsetX:Number) {};

//a teraz fragment podmiany:
setXAlign = Delegate.create(this, alignXBehavior.setXAlign);

//przy onresize:
setXAlign(MC,offsetX);

//wewnątrz (i na zewnątrz) przypisanej metody:
private var _stageresizer:StageResizer;
public setXAlign(MC:MovieClip,offsetX:Number) {
//
trace([MC,_stageresizer._actualWidth05,offsetX])
}

a w output:

undefined,477(lub inne wartości, zależnie od wielkości Stage'u),undefined

kto mi powie, gdzie był błąd ? a dokładniej (podpowiem :) ) - dwa (w zasadzie - jeżeli liczyć odrębnie każdą zmienną, to cztery, a jak się ktoś uprze, to nawet sześć - całkowicie pojedyńczo) ?

Komentarze (0)
StageResizer - problemy z singletonem
 Oceń wpis
   

Singleton to konstrukcja (wzorzec) zabezpieczająca nas przed stworzeniem więcej niż jednej instancji klasy. Jak ważny jest singleton, przekonałem się przy tworzeniu klas centrujących.

Korzystają one z raz wyliczonej w SR pozycji _actualWidth05 i _actualHeight05. Ponieważ żeby nie komplikować sobie sprawy z delegacją, skorzystałem z klasycznego rozwiązania wzroca strategii - podmieniam klasę - behaviora. Przez co muszę przekazać jej klip, na którym działa, oraz wartość offsetu.

W przypadku klas centrujących (i nie tylko ich, ale o tym za niedługo) muszą one także pobrać referencję do instancji SR. No więc... grzecznie i pięknie:

 public function SR_AlignX_Center () {
  //
  _stageresizer = StageResizer.getInstance ();
 }

a tak wygląda metoda SR.getInstance():

 public static function getInstance ():StageResizer {
  //
  if (myInstance == undefined)
  {
   trace ("creating new stageresizer by Singleton.");
   myInstance = new StageResizer ();
  }
  //trace ("return instance"); 
  return myInstance;
 }

efekt ? jak widać:

256 levels of recursion were exceeded in one action list.
This is probably an infinite loop.
Further execution of actions has been disabled in this movie.

Dlaczego ?

Ponieważ w kostruktorze wywołuję metodę, która definiuje podstawowe elementy tablicy ustawień - a jednym z nich jest właśnie SR_AlignX_Center, którego konstruktor zacytowałem na początku. Konstruktor ten jak ognia potrzebuje referencji do SR, a ta zaś dopiero się wywołuje, więc nie jest ustawiona. Skoro nie jest ustawiona, to znowu się wywołuje, a skoro znowu się wywołuje, to znowu definiuje elementy tablicy... i tak w koło Macieju - aż do 256 wywołań.

Jaki z tego wniosek ? myInstance dostaje referencję do tworzonej instancji dopiero po zakończeniu wywoływania konstruktora new. Co powoduje, że przez cały czas, przez kolejne polecenia zawarte w konstruktorze ma ona wartość undefined. No i dostajemy efekt (i output) jak wyżej...

Jak temu zapobiec ?

Na początku w konstruktorze SR dałem trace([myInstance,this]);

undefined,[object Object]

i to był strzał w dziesiątkę - już wiedziałem, gdzie jestem. Wystarczyło dodać jedną linię do konstruktora... oto on:

 private function StageResizer () {
  //
  myInstance = this;
  //
  _listeners = new Array ();
  setStageAreaAtStart (Stage.width, Stage.height);
  setStandardModes ();
  Stage.scaleMode = "noScale";
  Stage.align = "TL";
  Stage.addListener (this);
 }

co oczywiście pozwala też wywalić przypisanie do myInstance w metodzie SR.getInstance - no ale niech już to zostanie - w końcu tak jest klasycznie i niech tak zostanie - z przypisaniem w metodzie.

No to jeszcze tylko zostało ustawić pozostałe metody ustawiające (masło maślane) i będzie po bólu...

Komentarze (0)
StageResizer - final countdown
 Oceń wpis
   

Uff... po kilku tygodniach tworzenia (z przerwami na projekty "inne"), jeszcze trochę na bakier z wzorcami projektowymi - SR zaczyna odpowiadać. Nie pomyślałbym, że tak mnie będzie cieszyć widok takiego outputu:

system\stageresizerPackage\$test\stageresizer_test.fla:
creating new stageresizer by Singleton.
T% - unknown offset mode
T% - unknown scale mode
//--------
B% - unknown offset mode
B% - unknown scale mode
//--------
SR_Offset_NoOffset
SR_OffsetY_Bottom
B - unknown scale mode
//--------
L% - unknown offset mode
L% - unknown scale mode
//--------
SR_OffsetX_Left
SR_Offset_NoOffset
L - unknown scale mode
//--------
R% - unknown offset mode
R% - unknown scale mode
//--------
SRL_OffsetX_Right
SR_Offset_NoOffset
R - unknown scale mode
//--------
SR_OffsetX_Left
SRL_OffsetY_Top
TL - unknown scale mode
//--------
SRL_OffsetX_Center
SRL_OffsetY_Top
TC - unknown scale mode
//--------
SRL_OffsetX_Right
SRL_OffsetY_Top
TR - unknown scale mode
//--------

CL - unknown align mode
CL - unknown offset mode
CL - unknown scale mode
//--------
SRL_OffsetX_Right
SR_OffsetY_Middle
CR - unknown scale mode
//--------
SRL_OffsetX_Center
SR_OffsetY_Middle
CC - unknown scale mode
//--------
SR_OffsetX_Left
SR_OffsetY_Bottom
BL - unknown scale mode
//--------
SRL_OffsetX_Center
SR_OffsetY_Bottom
BC - unknown scale mode
//--------
SRL_OffsetX_Center
SR_OffsetY_Bottom
BR - unknown scale mode
//--------
% - unknown offset mode
//--------
%H - unknown align mode
%H - unknown offset mode
%H - unknown scale mode
//--------
%V - unknown align mode
%V - unknown offset mode
%V - unknown scale mode
//--------

Wszystko jest dokładnie tak jak trzeba - cho widać w niektórych miejscach jeszcze "SRL" - to oznaczenie klas ustawiających, które kiedyś były podpinane "poziom niżej" - dopiero przy inicjacji StageResizerListener ustawiane. Nie ma się też co martwić o "unknown mode" - oznacza to tylko tyle, że wartości podane są nieobsługiwane - to także "odziedziczone" po SR spod AS1 - teraz wszystko jest obsługiwane: jeżeli klasa zarządzająca nie znajdzie w swoich zbiorach odopwiednich instancji, to podstawi domyślne wartości. W przypadku wartości skalowanych proporcjonalnie ("%") po przystąpieniu do projektu w wersji as2 postanowiłem, że "%" będzie zawsze na drugim miejscu. No ale nie jest to żadna przeszkoda - StageResizer jest wyposażony w metody "map[]Mode" - które w bardzo prosty sposób pozwalają na przypisanie łańcuchowi "%H" metody spod łańcucha "H%".

No to będzie teraz troszeczkę dłuższa przerwa... tak do jutra :) - ponieważ czeka mnie zdefiniowanie (ponowne) klas odpowiedzialnych za ustaniewienia - najprostsze będą "top" i "left" - no może troszkę ustąpią miejsca pustym klasom "none" :).

Klasy zajmujące się skalowaniem zostawiam na koniec... to sama przyjemność:)

 Założenia projektu (link do wpisu).

//EDIT: dodatek nt. dylematu z poprzedniego wpisu - StageResizer.setListenerAlignMode(MC,XY); pozwala na zmianę align przez podanie referencji do zarejestrowanego MovieClipu; StageResizerListener.setAlignMode(XY); ustawia align konkretnej instancji -i wilk syty, i owca cała.

Komentarze (0)
StageResizer - "maleńkie" zmiany na zewnątrz
 Oceń wpis
   

No i doszedłem do momentu, w którym drogi starego i nowego StageResizera całkowicie się rozchodzą.

Powód ? Prosty: oszczędność czasu.

Dodanie słuchacza w starym SR:

stageresizer.addListener(MC:MovieClip,położenie:String,skaluj:Boolean,[włączAutoOffset:Boolean] | [wyłączAutoOffset:Boolean, offsetX:Number, offsetY:Number] )

zwraca: nic

dodanie słuchacza w nowym SR:

stageresizer.addListener(MC:MovieClip,położenie:String [, skalowanie:String]:StageResizer

 zwraca: jak widać, jest to referencja do instancji stageresizera.

Spostrzegawczy już zdążyli zauważyć, że zniknął offset przy dodaniu listenera. Dlaczego ?

Ponieważ mogę wywołać tylko: sr.addListener(MC,"BL"); i będzie to wystarczające - resztą zajmie się już klasa projektu. No a co, jeżeli potrzebuję ustawić ręcznie offset ? W tym pomoże nam zwracana referencja:

sr.addListener(MC,"BL").setListenerOffset(MC,offstx, offsty);

mam mały dylemat, czy nie zwracać referencji do StageResizerListener - czyli klasy "opiekującej się" słuchaczem - można by wtedy wywołać zmianę offsetu jeszcze krócej:

sr.addListener(MC,"BL").setOffset(offstx, offsty);

z drugiej strony, metodę sr.setListenerOffset(MC, offstx, offsty); można wywołać bezpośrednio... coprawda w moich założeniach było, że instancje klasy StageResizerListener będą prywatnymi, tylko dla SR... mam mały dylemat :)

Założenia projektu (link do wpisu).

//EDIT: dylemat rozwiązany - "upubliczniam" klasę StageResizerListener

Komentarze (0)
StageResizer - jak to wygląda ?
 Oceń wpis
   

Jeżeli ktoś byłby zainteresowany, jak wygląda projekt w działaniu, niech kliknie tutaj - musisz zmienić obszar okna przeglądarki, żeby zobaczyć ją w działaniu. Znajduje się tam wersja wcześniejsza SR, niezoptymalizowana (dziedzicząca z wersji spod as1) i nie-OOPowa. W każdym razie "tak ma to działać".

Pierwsze uruchomienie wersji OOP

Praktycznie przepisałem wczoraj wszystko od zera. Ufff... po pięciu godzinach siedzenia nad kodem kompilator nareszcie nie pluł się o dziedziczenia, prywatne członki i inne odnogi (statyczne lub nie) metod, funkcji i klas.

No i przyszła wreszcie ta chwila, kiedy mogłem kliknąć "test project" - i... nic. Tym razem przeszedłem sam siebie kombinując tak skutecznie, że nawet kompilator dał się oszukać, iż wszystko jest okey... cdn.

Założenia projektu (link do wpisu).

//EDIT: jakby ktoś pytał... od ostatniej publikacji elementów projektu parę rzeczy (metod, argumentów)  się zmieniło...

Komentarze (0)
StageResizer - StageResizerListener: przechowywanie obiektów
 Oceń wpis
   

Najprościej obiekty słuchaczy można by przechowywać pakując je w listę. Jeden push() i po bólu. Gorzej z wyjmowaniem :). Co wobec tego zrobić ? Z czego skorzystać ? Od wersji MX podstawowa klasa Array oficjalnie dziedziczy po Object - czyli można już nie tylko przypisywać elementy od 0..n, ale także podobnie jak w obiekcie, przypisywać je poprzez stringi. Tablice tego typu to tzw. "hash" - czyli tablice asocjacyjne. Tu przydatne z tego powodu, że bardzo łatwo dzięki nim obiekt wyjmiemy z listy, nie tracąc kolejności. Gdyby nie było tablicy asocjacyjnej, musielibyśmy przez push rejestrować obiekt, który pamiętał by swoją pozycję, musiałby ją uaktualniać przy każdym wyrejestrowaniu innego obiektu przed nim... itd. itp. - słowem mnóstwo roboty.

Do przechowywania informacji o słuchaczu posłuży nam klasa StageResizerListener. To instancje tej klasy będziemy wrzucać do "worka" tablicy.

_listeners[String(MC)]  = new StageResizerListener(MC);

a oto jej interfejs:

//package system.stageresizerPackage.istener;
//
interface stageresizerPackage.interfaces.IStageResizerListener {
 //
 function setScaleX (xscale:Number);
 function setWidth (w:Number);
 function setOffsetX (x:Number);
 function setX (x:Number);
 function multiplyX (sx:Number);
 //
 function setScaleY (yscale:Number);
 function setHeight (h:Number);
 function setOffsetY (y:Number);
 function setY (y:Number);
 function multiplyY (sy:Number);
 //
 function onresize ();
 function StageResizerListener (_MC:MovieClip);
}

Podobnie do przechowywania metod ustawiających - align, offset, scale - jako kontenerka użyję instancji odpowiednio przygotowanych klas. Dlaczego tak, a nie po prostu od razu referencji ? bo pamiętam o tym, że cokolwiek wrzucisz do tablicy bezpośrednio, będzie typu Object - programiści tego nie lubią :) wobec tego pierw zgodność typów, a potem do worka.

//package system.stageresizer.listener;
//
import stageresizerPackage.interfaces.IStageResizer_Scale;
//
class stageresizerPackage.ScaleModesArrayItem {
 //
 public var scaleBehavior:IStageResizer_Scale;
 //
 public function ScaleModesArrayItem (_scalebehavior:IStageResizer_Scale) {
  //
  scaleBehavior = _scalebehavior;
 }
}

//package system.stageresizer.listener;
//
import stageresizerPackage.interfaces.IStageResizer_OffsetX;
import stageresizerPackage.interfaces.IStageResizer_OffsetY;
//
class stageresizerPackage.OffsetModesArrayItem {
 //
 public var XBehavior:IStageResizer_OffsetX;
 public var YBehavior:IStageResizer_OffsetY;
 //
 public function OffsetModesArrayItem (_xbehavior:IStageResizer_OffsetX, _ybehavior:IStageResizer_OffsetY) {
  //
  XBehavior = _xbehavior;
  YBehavior = _ybehavior;
 }
}

Klasa do przechowywania alignów wygląda prawie że identycznie, więc ją pominę.

Założenia projektu (link do wpisu).

Komentarze (0)
StageResizer - pozostałe interfejsy
 Oceń wpis
   

Co robi interfejs ? W skrócie: zmusza nas do zdefiniowania w klasach implementujących interfejs metod zapisanych w interfejsie.

Po co w projekcie w typach zmiennych ? Pozwala to na podpięcie pod określoną zmienną n-różnych klas, pod warunkiem że będą implementować określony interfejs.

Co to daje ? Użycie interfejsu dla alignMode, scaleMode, offsetMode spowoduje, że nie popełnimy fatalnej pomyłki dodając klasę zajmującą się położeniem do zbioru klas zajmujących się skalowaniem obiektu.

Czy warto ? Skoro Arthwood mówi, że warto, to czemu mu nie wierzyć ? :)

//package system.stageresizer.listener;
//
interface stageresizerPackage.interfaces.IStageResizer_AlignY {
 //
 function setYAlign (offsetY:Number);
}

//package system.stageresizerPackage.listener;
//
interface stageresizerPackage.interfaces.IStageResizer_OffsetX {
 //
 function getXOffset (MC:MovieClip);
}

interface stageresizerPackage.interfaces.IStageResizer_Scale {
 //
 function setScale ();
}

Do gromadzenia sposobów ustawiania używam wzorca strategia, a to wymaga, by przekazywać do "magazynu" instancję klasy stworzoną za pomocą słowa "new". Jednym słowem odpada możliwość przekazywania w konstruktorze referencji do słuchacza - zresztą i tak niepotrzebnej, ponieważ będziemy nadpisywać tylko wywoływaną metodę, a nie przekazywać całą klasę. Dlaczego nie statycznie, tylko przez instancję ? Spróbuj użyć interfejsu do definiowania klasy statycznej :).

Jak widać po offsecie, w tym przypadku metoda predefiniowana jest metodą pobierającą. Pozostaje kwestią nierozwiązaną, czy żądać w interfejsie metody ustawiającej wartość offsetu.
W głównym projekcie offset "ręczny" zamierzam wrzucać do odpowiednich zmiennych instancji słuchacza - nie za bardzo więc będzie potrzebna odrębna metoda ustawiająca.

Najmniej zachodu jest z interfejsem klas skalujących - jedna metoda, zero parametrów - kompletny luz :). I pomyśleć, że będzie wszystko biegało na podobnych, prawie takich samych, zasadach.

Założenia projektu (link do wpisu).

Komentarze (0)
StageResizer - główny interfejs
 Oceń wpis
   

Jakie powinien posiadać metody publiczne ? Prywatne to dopiero będą się pojawiać przy zakładaniu klasy :).

Dwie podstawowe: dodanie słuchacza i zwolnienie słuchacza. Przyjmuję założenie, że słuchaczami mają być obiekty "ślepe" - czyli takie, które nie będą posiadały obsługi zdarzeń. To znaczy: grafik robi Ci top, do którego praktycznie nic nie musisz dodawać - ba, nawet masz dokładnie określone miejsce na scenie.

Trzy, cztery zarządzające sposobem ustawienia na scenie - odpowiednio align, offset, scale. Cztery, ponieważ listener może mieć niestandardowe ustawienie offsetu.

Podobnie rozszerzające - także 3-4 metody.

Ewentualne alternatywne podpinanie klipów z własną obsługą eventów ? czemu nie.

//package system.stageresizerPackage;
//
interface stageresizerPackage.IStageResizer {
 //
 function updateListener (MC:MovieClip):Boolean;
 function setStageAreaAtStart (startwidth:Number, startheight:Number);
 //
 function addAlignMode (mode:String, alignXClass:IStageResizer_AlignX, alignYClass:IStageResizer_AlignY);
 function addOffsetMode (mode:String, offsetXClass:IStageResizer_OffsetX, offsetYClass:IStageResizer_OffsetY);
 function addScaleMode (mode:String, scaleClass:IStageResizer_Scale);
 //
 function setListenerScaleMode (MC:MovieClip, mode:String);
 function setListenerAlignMode (MC:MovieClip, mode:String);
 function setListenerOffsetMode (MC:MovieClip, mode:String);
 function setListenerOffset (MC:MovieClip, offsetx:Number, offsety:Number);
 function addListener (MC:MovieClip, onstagepos:String);
 function removeListener (MC:MovieClip);
 function addEventListener (MC:MovieClip);
 function removeEventListener (MC:MovieClip);
}

Dodanie i zwolnienie słuchacza to addListener i removeListener. Dodanie i zwolnienie klipów posiadających własną obsługę zdarzeń to addEventListener i removeEventListener. To tylko alternatywa, którą teraz nie będę rozwijał - po prostu podany klip dorzucę do listy słuchaczy Stage'a.

Klasa główna będzie posiadać predefiniowane (za pomocą metod publicznych rozszerzających) rodzaje sposobów ustawienia. Ponieważ na początku przy dodaniu słuchacza jest określana tylko jedna wartość domyślna (onstagepos), będą dostępne cztery metody modyfikujące - setListenerAlignMode, setListenerOffset, setListenerOffsetMode, setListenerScale. Jak już pisałem wcześniej, offset można ustawić ręcznie za pomocą drugiej metody.

Nacisk, który jest położony na dodawanie i mapowanie klas określających położenie - tzn. użycie zmiennych określonego typu interfejsów - dodałem specjalnie, by przypadkowo podpięcie innej metody nie spowodowało "wylecenie w powietrze" aplikacji.

Dzięki takiemu dodawaniu własnych metod nie musisz się bawić w rozsszerzanie, modyfikowanie, przekształcanie - ot, "rejestrujesz" własną klasę spełniającą określone warunki i obiekty zachowują się zgodnie z Twoimi założeniami.

Czasami mam wrażenie, że programowanie obiektowe broni programistę przed nim samym :)

//EDIT: ostatnia zmiana: addEventListener (i logicznie removeEventListener) - typ słuchacza zmieniony na Object

Komentarze (0)
1 | 2 |

Najnowsze komentarze
2014-01-06 19:33
najlepszeprezenty.com.pl:
Flash After Party czyli o flashu w realu [EDIT]
pozdrowienia :)
2013-12-25 22:07
Powiększanie penisa:
Warto blogować ?
Trudno się z tym nie zgodzić, przez to, że są bardzo ogólne to także są bardzo trafne.
2013-12-08 22:47
ets2:
Warto blogować ?
Wartościowych blogów jest na naprawdę mało, sam staram się coś stworzyć a czy wyjdzie to się[...]
O mnie
MaW: flash, gry i cała reszta
Po prostu flashmaniak.