Category Porady i solucje

Prosto z Commodore 64: efekt plazmy

Od pewnego czasu wracam wspomnieniami do czasów demosceny, a od kilku dni odświeżam stare umiejętności i przekładam to, co kiedyś robiło się w assemblerze, na Flasha i Actionscript. Jednym z wynalazków koderów z demosceny, był efekt plazmy.

Opisowo efekt ten polegał na uzyskaniu interesujących graficznie animacji “rozlewających” się kolorów. Ponieważ jednak ówczesne komputery były zbyt wolne na obliczanie co ramke wartości każdego pixela na ekranie, koderzy użyli jednej ze swoich sztuczek: tylko raz na starcie rysowało się obraz pixeli na ekranie, a potem podmieniano jedynie paletę kolorów używaną przez układ graficzny. W ten sposób nawet ZX-Spectrum dawało radę:

W takim razie, skoro ZX-Spectrum ze swoim Zilogiem Z80 dawało radę, jak poradzi sobie flash na procesorach taktowanych gigaherzami? :) Nie możemy niestety operować bezpośrednio na palecie ( zabawa z ColorTransform również odpada), więc jednak musimy za każdym razem odrysować wszystkie pixele na ekranie. Spróbujmy:

package {
	import flash.display.Sprite;
	import flash.display.StageScaleMode;
	import flash.display.StageAlign;
	import flash.events.Event;
	import flash.display.BitmapData;
 
	[SWF(width="300", height="300", backgroundColor="0x000000")]
	public class PlasmaEffect extends Sprite
	{
		private var w:int = 150;
		private var h:int = 150;
		private var colors:Array = new Array();
		private var plasma:Array = [w];
		private var paletteShift:int;
 
		public function PlasmaEffect()
		{
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			this.stage.align = StageAlign.TOP_LEFT;
			this.stage.frameRate = 10;
			this.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
 
			generatePalette();
			generatePlasma();
		}
 
		private function draw():void
		{
			this.graphics.clear();
 
	        for(var x:int = 0; x < w; x++)
	        for(var y:int = 0; y < h; y++)
	        {
	            this.graphics.lineStyle(1, colors[(plasma[x][y] + paletteShift) % 256]);
	            this.graphics.moveTo(x * 2, y * 2);
	            this.graphics.lineTo(x * 2, y * 2 + 1);  
	        }			
		}
 
 
		private function generatePlasma():void
		{
			var colorIndex:int;
 
		    for(var x:int = 0; x < w; x++)
		    {
		    	plasma[x] = [h];
 
			    for(var y:int = 0; y < h; y++)
			    {		    	
			        colorIndex =
			        (
			              128.0 + (128.0 * Math.sin(x / 16.0))
			            + 128.0 + (128.0 * Math.sin(y / 32.0))
			            + 128.0 + (128.0 * Math.sin(Math.sqrt(((x - w / 2.0)* (x - w / 2.0) + (y - h / 2.0) * (y - h / 2.0))) / 8.0))
			            + 128.0 + (128.0 * Math.sin(Math.sqrt((x * x + y * y)) / 8.0))
			        ) / 4;			        
 
			        plasma[x][y] = colorIndex;       
			    }			
		    }
		}
 
		private function generatePalette():void
		{
			var r:int;
			var g:int;
			var b:int;
 
			for(var x:int = 0; x < 256; x++)
			{
			    r = 128.0 + 128 * Math.sin(3.1415 * x / 16.0 );
			    g = 128.0 + 128 * Math.sin(3.1415 * x / 128.0 );
			    b = 0;
			    colors.push(r * 256 * 256 + g * 256 + b);
			} 			
		}
 
		private function onEnterFrame(e:Event):void
		{
			draw();	
			paletteShift += 1;
		}
	}
}

No cóż, na moim, całkiem współczesnym komputerze nie daje rady – ledwo 5 klatek na sekundę, i macierz pixeli o rozmiarach 150×150. Winowajcą jest metoda lineTo – zbyt wolna jak na nasze potrzeby.
Ale, drobna zmiana kodu, zastąpienie rysowanie poprzez graphics.lineTo na bezpośrednie ustawianie pixelów bitmapy:

package {
	import flash.display.Sprite;
	import flash.display.StageScaleMode;
	import flash.display.StageAlign;
	import flash.events.Event;
 
	import flash.display.BitmapData;
	import flash.display.Bitmap;
 
	[SWF(width="300", height="300", backgroundColor="0x000000")]
	public class PlasmaEffect extends Sprite
	{
		private var w:int = 300;
		private var h:int = 300;
		private var colors:Array = new Array();
		private var plasma:Array = [w];
		private var paletteShift:int;
 
		private var bitmap:BitmapData = new BitmapData(w, h, false);
 
		public function PlasmaEffect()
		{
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			this.stage.align = StageAlign.TOP_LEFT;
			this.stage.frameRate = 20;
			this.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
 
			this.addChild(new Bitmap(bitmap));
 
			generatePalette();
			generatePlasma();
		}
 
		private function draw():void
		{
			this.graphics.clear();
 
	        for(var x:int = 0; x < w; x++)
	        for(var y:int = 0; y < h; y++)
	        {
	        	bitmap.setPixel(x, y, colors[(plasma[x][y] + paletteShift) % 256]);
	            //this.graphics.lineStyle(1, colors[(plasma[x][y] + paletteShift) % 256]);
	            //this.graphics.moveTo(x * 2, y * 2);
	            //this.graphics.lineTo(x * 2, y * 2 + 1);  
	        }			
		}
 
 
		private function generatePlasma():void
		{
			var colorIndex:int;
 
		    for(var x:int = 0; x < w; x++)
		    {
		    	plasma[x] = [h];
 
			    for(var y:int = 0; y < h; y++)
			    {		    	
			        colorIndex =
			        (
			              128.0 + (128.0 * Math.sin(x / 16.0))
			            + 128.0 + (128.0 * Math.sin(y / 32.0))
			            + 128.0 + (128.0 * Math.sin(Math.sqrt(((x - w / 2.0)* (x - w / 2.0) + (y - h / 2.0) * (y - h / 2.0))) / 8.0))
			            + 128.0 + (128.0 * Math.sin(Math.sqrt((x * x + y * y)) / 8.0))
			        ) / 4;			        
 
			        plasma[x][y] = colorIndex;       
			    }			
		    }
		}
 
		private function generatePalette():void
		{
			var r:int;
			var g:int;
			var b:int;
 
			for(var x:int = 0; x < 256; x++)
			{
			    r = 128.0 + 128 * Math.sin(3.1415 * x / 16.0 );
			    g = 128.0 + 128 * Math.sin(3.1415 * x / 128.0 );
			    b = 0;
			    colors.push(r * 256 * 256 + g * 256 + b);
			} 			
		}
 
		private function onEnterFrame(e:Event):void
		{
			draw();	
			paletteShift += 1;
		}
	}
}

i… sukces! :) Tym razem macierz 300×300 i 20 klatek na sekundę:

daje radę :)

getURL w ActionScript 3

Dobrze znanej z ActionScript2 metody getURL nie znajdziemy w wersji 3 języka. Zamiast niej możemy posłużyć się metodą

flash.net.navigateToURL(request:URLRequest, window:String):void

Rzucanie cieni w Papervision3D

Andy Zupko, współautor Papervision3D, napisał klasę dzięki której dostajemy możliwość rzucania cieni przez dowolne obiekty na naszej scenie 3D. Muszę przyznać, że działa to znakomicie – oraz dużo szybciej od pierwszej wersji, którą zaprezentował w ubiegłym roku. Klasa ShadowCaster nie została na razie dodana do pakietu z Papervision3D, ale można pobrać jej źródło bezpośrednio ze strony Andiego – tam też znajdziecie demo i przykład wykorzystania.

Wywoływanie metod pomiędzy plikami SWF

Problem: Plik swf utworzony w actionscript2 będzie osadzony w innym pliku swf utworzonym w actionscript3. W jaki sposób z jednego pliku swf wywołać metodę w drugim?

Rozwiązaniem jest użycie klasy LocalConnection. Oto przykładowy kod:

embeded.swf (actionscript 2):

on(release) {
	var localConnection:LocalConnection = new LocalConnection();
	localConnection.send("holderConnection", "myMethod");
}

holder.swf (actionscript 3):

import flash.net.LocalConnection;
 
public class Holder
{
    public function Holder()
    {
        var localConnection:LocalConnection = new LocalConnection();
	localConnection.client = this;
	localConnection.connect("holderConnection");
    }
 
    public function myMethod():void
    {
        trace('metoda myMethod została wywołana');
    }
}

Należy zwrócić uwagę, aby metody które chcemy wywołać były zdefiniowane jako metody publiczne.

Preprocesor dla ActionScript

Preprocesor to narzędzie, które parsuje kod źródłowy programy przed poddaniem go kompilacji. Umożliwia to np. utworzenie warunkowych fragmentów kodu, które w specyficznych sytuacjach są dołączane do pliku wynikowego. Niestety, flex nie udostępnia nam takiego mechanizmu. Możemy jednak sami podpiąć zewnętrzny preprocesor.

Proponuję do tego celu użyć np. programu cpp (C Preprocesor). Jest to chyba jeden z bardziej rozbudowanych tego typu programów. W systemach Linux jest on prawie na pewno preinstalowany, w Windows możemy go uruchomić dzięki pakietowi Cygwin. Oczywiście cpp nie jest jedynym zdatnym dla nas preprocesorem – możemy w jego miejsce zastosować inny.

OK, a więc teraz pora na mini HOW-TO – jak podpiąć cpp do FlexBuildera – wersja dla Windows:

W pierwszym kroku należy ściągnąć i zainstalować cygwin, oraz doinstalować do niego kompilator C (to właśnie w nim znajduje się cpp).

Następnie utwórz skrypt:

c:
cd c:\cygwin\bin\
 
FOR /R %1 %%F IN (*.as) DO (
	@COPY /Y "%%F" "%%F.cpp-bak"
	cpp.exe -E -P -C '%%F' -o '%%F.cpp-processed'
	@MOVE /Y "%%F.cpp-processed" "%%F"
)

i zapisz go pod nazwą cpp.bat. Utwórz drugi skrypt:

@ECHO off
 
FOR /R %1 %%F IN (*.as) DO (
	@MOVE /Y "%%F.cpp-bak" "%%F"
)

i nazwij go cpp_restore.bat.

Teraz, w FlexBuilder, w ustawieniach projektu wybierz zakładkę Builders i utwórz nowego “budowniczego” klikając w przycisk New. W okienku, które się otworzy, w polu Name: wpisz nazwę np. Preprocesor, w polu Location: wpisz pełną ścieżkę do skryptu cpp.bat, a w polu Arguments: wpisz “${project_loc}”. Zatwierdź wciskając OK.

Teraz dokładnie to samo zrób dla drugiego skryptu, podaj tylko inną nazwę np. Preprocesor – restore.

Gdy już utworzysz obydwu “budowniczych”, ustaw ich w następującej kolejności:
- Preprocesor
- Flex
- Preprocesor – restore

Zrobione. Teraz możesz już używać instrukcji preprocesora w swoim kodzie. Dokumentację do niego znajdziesz pod adresem gcc.gnu.org/onlinedocs/cpp/.

Uwaga
Jest pewna wada przedstawionego rozwiązania. Ponieważ preprocesor usuwa linie zawierające jego rozkazy, wobec tego numery linii w komunikatach o błędach ActionScript nie będą się pokrywały z rzeczywistością. Rozwiązaniem byłoby wymuszenie na preprocesorze wstawiania pustych linii w miejsce usuniętych – niestety na razie nie wiem jak to skutecznie zrobić.

Co to jest Apollo?

Ciekawy post na blogu Mike’a Chambers wyjaśniający czym tak właściwie jest, i do jakich celów zostało wymyślone Apollo:
http://weblogs.macromedia.com/…/why_apollo.html

UPDATE: To oczywiście bardzo stary post – obecnie apollo zostało przemianowane na Adobe Air – Adobe Integrated Runtime

Szerokość i wysokość aplikacji actionscript 3

Bawiąc się dalej FlexBuilderem, natrafiłem na problem z ustawieniem rozmiarów aplikacji actionscript. W ustawieniach projektu nie znajdziemy odpowiednich opcji, nie da się ich również podać w argumentach kompilatora. Okazuje się, że robi się to poprzez metatag [SWF] wstawiony przed definicją głównej klasy projektu:

package
{
  [SWF(width="800", height="600", backgroundColor="#ffaa22")]
  public class MyApp extends Sprite  {
    //...
  }
}

Osadzanie fontów w aplikacji actionscript 3

ActionScript 3 pozwala na osadzanie (embedding) fontów bezpośrednio w aplikacji. Co by się zbytnio nie rozwodzić oto działające rozwiązanie:

public class Epeiron2 extends Sprite {
[Embed(source="c:/windows/fonts/AstalametPure.ttf", fontFamily="AstalametPure")]
 
private var fontTemp:String;
 
var format:TextFormat = new TextFormat();
format.font='AstalametPure';
 
textField1:TextField=new TextField();
textField1.defaultTextFormat=format;
}

Zauważcie, że w wersji beta metatag Embded był umieszczany poza klasą, w sekcji package. Obecnie jednak należy go umieszczać tak jak na przykładzie powyżej.

Linia:

private var fontTemp:String;

może budzić zdziwienie ale jest konieczna właśnie w tym miejscu – ponieważ osadzony plik jest przypisywany do najbliższej zmiennej.

Można także zabudować font, nie podając ścieżki do niego, ale jego nazwę. Oczywiście taka nazwa musi być zarejestrowana w systemie:

[Embed(systemFont="Astalamet Pure", fontFamily="AstalametPure")]

O banialukach w stylu “osadzanie fontów powoduje zwiększenie objętości pliku swf” nie będę pisał :)

Selektywne cachowanie w SMARTY

System szablonów Smarty posiada wbudowane cachowanie stron. Mechanizm ten znakomicie przyspiesza działanie serwisów, które muszą działać pod dużym obciążeniem. Dzięki możliwości sprawdzania, które fragmenty strony pozostają w pamięci podręcznej, można radykalnie zmniejszyć liczbę zapytań do bazy danych jak i odciążyć PHP od parsowania szablonów.

Problem może jedynie stworzyć sytuacja, w której chcemy aby fragment strony pozostawał nie cachowany – może to być np. aktualne notowania, dynamiczna reklama czy cokolwiek innego. Jeżeli chcielibyśmy taki fragment strony dołączać za pomocą funkcji {include}, to niestety i ona będzie podlegać cachowaniu.

Zamiast jednak używania {include}, możemy wykorzystać funkcję {insert}, która z poziomu szablonu wywoła wskazaną (w parametrze name) funkcję PHP i umieści w szablonie rezultat działania funkcji. Po szczegóły zapraszam do dokumentacji, a poniżej podaję przykład gotowego rozwiązania.

Tworzymy funkcję w PHP, która będzie zwracała przetworzony przez Smarty, wskazany w parametrze szablon:

function insert_nocache($PARAMS,$smarty) {
 
//wyłączamy cachowanie
$smarty-&gt;caching = 0;
 
//pobieramy i zwracamy przetworzony szablon
return $smarty-&gt;fetch($PARAMS['file']);
}

A tutaj już sposób wywołania tej funkcji z poziomu szablonu:

..tutaj część która jest cachowana..
{insert name=nocache file='plik_bez_cachowania.tpl'}
..i tutaj dalsza część która jest cachowana..

Brak obrazka tła na przyciskach w IE6

Problem: przycisk formularza wyświetla się w standardowy sposób, pomimo ustawienia w stylu obrazka dla tła tego przycisku.

Rozwiązanie: w IE nie wystarczy ustawić li tylko styl background-image, ale dodatkowo należy ustawić jakiś kolor tła background-color. Bez tego drugiego, obrazek nie zostaje wyświetlony (IE6, Windows XP SP2). Ot taka kolejna humoreska na temat przeglądarki Internet Explorer.

Copyright © Kubiczek devblog
I'm lovin' it ;)

Built on Notes Blog Core
Powered by WordPress