Prosto z Commodore 64: tunel

Kolejny sympatyczny efekt, wykorzystywany bardzo często w produkcjach demosceny to tunel. W milionach różnych wersji, wyewoluował od całkiem prostej realizacji rysowanych okręgów do hiper-super-triple teksturowanego tunelu z dynamicznym oświetleniem, cieniami i bajerami.

Realizacja tego efektu we flashu jest jak najbardziej możliwa. Sprawa wygląda prosto:

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
 
	[SWF(width="500", height="500", backgroundColor="0x000000")]
	public class TunnelEfect extends Sprite
	{
		private var w:int = 250;
		private var h:int = 250;
		private var texWidth:int = 256;
		private var texHeight:int = 256;
 
		private var texture:Array = new Array(texWidth);
		private var distanceTable:Array = new Array(w * 2);
		private var angleTable:Array = new Array(w * 2);
 
		private var animationShift:Number = 0;
 
		private var bitmap:BitmapData = new BitmapData(w * 2, h * 2, false, 0x000000);
 
		public function TunnelEfect()
		{
                        var angle:int, distance:int, ratio:Number = 32;
 
			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));			
 
			for(var x:int = 0; x < texWidth; x++)
			{
				texture[x] = new Array(texHeight);
 
				for(var y:int = 0; y < texHeight; y++)
				{
					texture[x][y] = ((x) ^ (y)) * 256;
				}			
			}			 
 
			for(x = 0; x < w * 2; x++)
			{
				distanceTable[x] = new Array(h * 2);
				angleTable[x] = new Array(h * 2);
 
			    for(y = 0; y < h * 2; y++)
			    {
			    	distance = (ratio * texHeight / Math.sqrt(((x - w) * (x - w) + (y - h) * (y - h)))) % texHeight;
			        angle = Math.abs(0.5 * texWidth * Math.atan2((y - h), (x - w)) / 3.1416);
 
			        distanceTable[x][y] = distance;
			        angleTable[x][y] = angle;
			    }			 
			}
		}
 
		private function draw():void
		{
			var shiftX:int, shiftY:int, shiftLookX:int, shiftLookY:int, color:Number;
 
			shiftX = (texWidth * 1.0 * animationShift);
			shiftY = (texHeight * 0.25 * animationShift);  
 
			shiftLookX = w / 2 + (w / 2 * Math.cos(animationShift));
	        shiftLookY = h / 2 + (h / 2 * Math.sin(animationShift * 2.0));	
 
	        for(var x:int = 0; x < w; x++)
	        {
		        for(var y:int = 0; y < h; y++)
		        {
				    color = texture[Math.abs(distanceTable[x + shiftLookX][y + shiftLookY] + shiftX)  % texWidth][Math.abs(angleTable[x + shiftLookX][y + shiftLookY] + shiftY) % texHeight];
		        	bitmap.setPixel(x * 2, y * 2, color);
		        }			
			}
		}
 
		private function onEnterFrame(e:Event):void
		{
			draw();	
			animationShift += 0.05;
		}
 
	}
}

A oto i gotowy tunel:

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ę :)

Kalkulator położenia słońca

Do większego projektu, potrzebowałem napisać program obliczający położenie (wysokość i azymut) słońca na nieboskłonie. O tym “większym” projekcie mam nadzieję, że napiszę niebawem, a na razie oddaję do dyspozycji gotowy kalkulator położenia słońca – parametry wejściowe to lokalizacja, data i godzina; wynik – azymut i wysokość. Program napisany w Adobe Flex.

FemtoPlayer – mini free mp3 flash player

Kilka dni temu szukałem malutkiego mp3 playera, który wyświetlał by jedynie ikonkę z przyciskiem play i odgrywał wskazany dźwięk mp3. Poszukiwania w googlu dały kilka rezultatów, ale każdy z nich albo był “za bardzo”, albo miał złą licencję. Zamiast tracić dalej czas napisałem te kilkanaście linijek kodu i stworzyłem swój własny FemtoPlayer – Femto to brzmi dumnie ;)

Parametry:

file = pathToMP3 //ścieżka do pliku mp3
autoplay = true|false //czy automatycznie odgrywać dźwięk po wczytaniu

Przykład użycia:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="20" height="25" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="FlashVars" value="file=test.mp3&amp;autoplay=true" /><param name="src" value="FemtoPlayer.swf" /><param name="wmode" value="transparent" /><param name="flashvars" value="file=test.mp3&amp;autoplay=true" /><embed type="application/x-shockwave-flash" width="20" height="25" src="FemtoPlayer.swf" wmode="transparent" flashvars="file=test.mp3&amp;autoplay=true"></embed></object>

SWF do pobrania: download

Lata ’90

Moja przygoda z programowaniem rozpoczęła się na początku lat 90, kiedy to istniała tzw. demoscena. Na owej scenie koderzy (programiści) w pocie czoła tworzyli tzw. dema czyli wg współczesnej nomenklatury programy multimedialne, obrazujące możliwości sprzętu, a przede wszystkim umiejętności tychże koderów. Ja sam, kodowałem wpierw na komputerze Commodore 64 z procesorem motorola 6502 oraz całkowitą pamięcią 64kB, a następnie na komputerach Amiga (500, 1200).

Ówczesne produkty demosceny, dzisiaj budzą albo niezrozumienie albo litość. Pamiętajmy jednak, że najlepsze komputery miały wtedy procesory z zegarem 4MHz i 1MB pamięci RAM (po rozszerzeniu), a sprzętowe wspomaganie 3D to był tylko sen wariata. Wszystkie te programy pisało się w assemblerze i optymalizowało dosłownie każdy cykl procesora. Wiele efektów powstawało w wyniku obchodzenia ograniczeń sprzętowych(SIC!) – czyli de facto wykorzystywania sprzętu w sposób, który nie został przewidziany przez jego projektantów. To były czasy!

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.

Pragmatyczny programista

Pragmatyczny programista – od czeladnika do mistrza – to obowiązkowa pozycja każdego programisty. Autorzy, Andrew Hunt i David Thomas, w przyjaznej formie przekazują zdrowe, a jednocześnie nadwyraz skuteczne podejście do programowania. W książce, oprócz powszechnie znanym pojęć jak hermetyczność czy programowanie asertywne, poznamy również takie jak zasada KISS (Keep It Simple, Stupid) czy też sposób wykorzystania Pocisków Smugowych.

Pomimo tego, że książkę przeczytałem już spory czas temu, wiem że nadal mogę ją śmiało polecać programistom na dowolnym etapie ich rozwoju :). Naprawdę – jako jedna z nielicznych trafi ona do tych początkujących jak i całkiem zaawansowanych, a język jakim jest napisana sprawi, że większość przeczyta ją od deski do deski z największą przyjemnością.

Pragmatyczny programista, Wydawnictwo Naukowo Techniczne, 2002.

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.

Co dalej z PHP

Rozwój PHP zatrzymał się w miejscu. Wersja 5 języka stanęła okrakiem między językami silnie typowanymi, a językami dynamicznymi, wprowadzając na dodatek kilka swoich udziwnień i ciągnąc bagaż wad z poprzednich wersji.

Wydawało by się, że skoro model języka wyraźnie bazuje na Javie (i C++), a Zend wydając wersję 5 powiedział “A”, to powinien powiedzieć “B”, i dokończyć kilka spraw:

  • Wprowadzić możliwość pełnego typowania parametrów – obecnie parametry mogą być typowane, tylko jeżeli typem jest klasa albo array.
  • Umożliwić tworzenie setterów i getterów – magiczne metody nie są wystarczającym rozwiązaniem, gdyż nie komunikują jasno jakie właściwości są obsługiwane.
  • Wprowadzić przeciążanie metod.

Poza tym należałoby się odciąć kreską od poprzednich wersji lub przynajmniej posprzątać, to co posprzątane nie zostało, a więc przede wszystkim:

  • niespójne nazewnictwo funkcji i sposób przekazywania parametrów
  • rozdzielić metody na przestrzenie nazw (podobno w PHP6)
  • wprowadzić pełną obsługę dla UTF

Niestety, PHP 6 nad którym prace podobno trwają od dwóch lat, przyniesie jedynie kosmetyczne poprawki. A więc język ten pozostanie językiem do niczego, cieszącym się popularnością jedynie z siły rozpędu.

Linki:

Lista “todo” dla PHP 6
Statystyki używalności PHP – od trzech lat popularność języka nie rośnie