Трехмерный план во флеш.


Видел недавно одну интересную игрулю - это гоночка на флеше. Пораспросив о технологии, оказалось, что это as2 закоденная в фреймах, однако, неужели на as3 неполучится сделать аналог, должен всех разочаровать на сегодняшнюю дату 14.02.2010 не берусь утверждать, что такое возможно... as3 тормозит больше чем as2??? Да нет, просто новый релиз плеера WIN 10,1,51,66 (бета) тормозит больше чем плеер восьмой версии, так адобы на сайте написали, что флеш плеер как никогда лучше поддерживает работу видеокарты, но как показала практика, на текущий момент у нас обратный эффект (в восьмой верии плеера игруля меньше нагружает проц, чем в 10.1 судя по индикатору загрузки проца виндоус). Но все-же... Для такого три-д фона я решил сделать супер оптимизацию, она состоит в том, что ничего не вычисляется впринципе, те. фиксированный фокус и фиксированный угол наклона плоскости обзора, при этом надо посщитать только uv координаты. Еще также прямоугольник экрана обрезан заранее и то чего не видно не отрисовуется, дополнительные классы для отображения не используются, так-что тормоза связанные с объектно ориентированным кодингом исключаются автоматически, плоскость обзора разбивается на фиксированное число треугольников, которое можно задать заранее. Я выбрал 5x5 (шутка, на самом деле 5x6 выбрать нельзя, можно 6x6, ну вы поняли...) Вот код
package  
{
	import flash.display.*;
	import flash.events.Event;
	import flash.geom.*;
	import flash.text.*;
	import lex.comp.blank.BSlider;
	/**
	 * ...
	 * @author Alex Lexcuk http://www.murmadillo.tut.su
	 */
	public class Doc extends Sprite
	{
		[Embed(source = 'rect2816.gif')]
		private var Pic:Class,
			bmd:BitmapData,
			bm:Bitmap;

		private var uvComposit:Vector.<Number>;
		private var uv:Vector.<Number>;
		private var sh:Sprite;
		private var sliderX:BSlider;
		private var sliderY:BSlider;
		private var basUV3:Vector.<Number>;
		private var _tri:Vector.<int>;
		private var _scr:Vector.<Number>;
		public function Doc() 
		{

			addChild(sliderX = new BSlider());
			sliderX.modifyGripToRect = true;
			sliderX.addEventListener(Event.CHANGE, sliderChangeHandler);
			
			addChild(sliderY = new BSlider());
			sliderY.x = 20;
			sliderY.modifyGripToRect = true;
			sliderY.addEventListener(Event.CHANGE, sliderChangeHandler);
			
			addChild(sh = new Sprite());
			
			bmd = new Pic().bitmapData;
			bm = new Bitmap(bmd);
			
			sh.x = 400;
			sh.y = 400;
			//sh.scaleX = 0.9;
			
			var i:int;
			//кол-во сегментов
			var amountSeg:int = 5;
			//четыре точки базовые
			var bas4:Vector.<Number> = Vector.<Number>([ -271, 0, -150,
			271, 0, -150,
			-3000, 0, 3000,
			3000, 0, 3000]);
			
			var kasFocus:Number = 2000;
			var top:Vector.<Number> = calcGap(bas4, 0, 1, amountSeg);
			var left:Vector.<Number> = calcGap(bas4, 0, 2, amountSeg, kasFocus);
			var right:Vector.<Number> = calcGap(bas4, 1, 3, amountSeg, kasFocus);
			
			var comboRL:Vector.<Number> = right.concat(left);
			var gridV:Vector.<Number> = new Vector.<Number>();
			//showV3d(top, 0xFF8040, 2);
			//showV3d(right, 0xFF0000, 5);
			for (i = 0; i <= amountSeg; i++) {
				gridV = gridV.concat(calcGap(comboRL, i, i+amountSeg+1, amountSeg))
			}
			//(gridV);
			//формирование унврап
			var uv:Vector.<Number> = new Vector.<Number>();
			
			var minX:Number = Number.MAX_VALUE;
			var maxX:Number = Number.MIN_VALUE;
			
			var minY:Number = Number.MAX_VALUE;
			var maxY:Number = Number.MIN_VALUE;
			
			for (i = 0; i < gridV.length; i+=3) {
				uv.push(gridV[i], gridV[i + 2]);
				if (minX >= gridV[i]) minX = gridV[i];
				if (minY >= gridV[i + 2]) minY = gridV[i + 2];
				
				if (maxX <= gridV[i]) maxX = gridV[i];
				if (maxY <= gridV[i + 2]) maxY = gridV[i + 2];
				
			}
			var scaleX:Number = maxX - minX;
			var scaleY:Number = maxY - minY;
			for (i = 0; i < uv.length; i += 2) {
				uv[i] = (uv[i] - minX) / scaleX;
				uv[i+1] = (uv[i+1] - minY) / scaleY;
			}
			trace(uv);
			//экранные координаты
			var v2:Vector.<Number> = showV3d(gridV, 0xFF0000, 3);
			//Массив в треугольками
			var tri:Vector.<int> = new Vector.<int>();
			var iT:int;
			trace('gridV.length '+gridV.length);
			for (i = 0; i < v2.length/2-amountSeg-2; i ++) {
				iT = i;
				if (iT!=amountSeg&&(iT-(int(iT/amountSeg)-1))/int(iT/amountSeg)!=amountSeg) {
					tri.push(iT , iT + 1, iT + amountSeg+1);
					tri.push(iT+1 , iT + amountSeg+2, iT + amountSeg+1);
				}
			}
			sh.graphics.beginBitmapFill(bmd);
			//tri = Vector.<int>([0, 1, 2]);
			
			sh.graphics.drawTriangles(v2, tri, uv);
			
			_tri = tri;
			_scr = v2;
			
			basUV3 = new Vector.<Number>();
			for (i = 0; i < uv.length; i += 2)
			basUV3.push(uv[i], uv[i + 1], 0);
		}

		
		
		//расчет промежутка для деления пространства на треугольники
		private function calcGap(v:Vector.<Number>, enteringBegin:int, enteringEnd:int, amountSeg:int, unFocus:Number=1):Vector.<Number> {
			var i:int;
			enteringBegin = enteringBegin * 3;
			enteringEnd = enteringEnd * 3;
			//линия
			var line:Vector.<Number> = new Vector.<Number>();
			//нормализированный вектор линии
			var n:Vector3D = new Vector3D(v[enteringEnd], v[enteringEnd+1], v[enteringEnd+2]);
			n = n.subtract(new Vector3D(v[enteringBegin], v[enteringBegin+1], v[enteringBegin+2]));
			//длина линии
			var lenVector:Number = n.length;
			n.normalize();
			var tv:Vector3D;
			var rTv:Vector3D;
			var nTemp:Vector3D;
			var w:Number;
			nTemp = n.clone();
			//формирование ряда
			for (i = 0; i <= amountSeg; i++) {
				tv = n.clone();
				rTv = n.clone();
				tv.scaleBy( (lenVector / amountSeg) * i);
				rTv = tv.clone();
				w = 1 / ((unFocus + tv.length) / unFocus);
				//('w '+w);
				if (unFocus != 1) {
					tv = n.clone();
					tv.scaleBy((lenVector - rTv.length)*w);
					////(lenVector*w);
				}
				tv = tv.add(new Vector3D(v[enteringBegin], v[enteringBegin+1], v[enteringBegin+2]));
				line.push(tv.x, tv.y, tv.z);
			}
			return line;
		}

		private function showV3d(_v:Vector.<Number>, color:uint,diam:Number):Vector.<Number> {
			var len:int, i:int, z:Number, x0:Number, y0:Number, z0:Number, w:Number;
			var m3d:Matrix3D = new Matrix3D();
			var v:Vector.<Number> = _v.concat();
			var focus:Number = 400;
			var scale:Number = 1;
			var v2:Vector.<Number> = new Vector.<Number>();
			m3d.appendRotation( 30, Vector3D.X_AXIS);
			
			m3d.transformVectors(v,v);
			
			len = v.length;
			for (i = 0; i < len; i += 3) {
				z = v[i + 2 ]*scale;//z
				w = 1 / ((focus + z) / focus);
				x0 = v[i ]*scale*w;//x
				y0 = v[i + 1 ]*scale*w;//y
				//z0 = v[i + 2 ] *scale* w;//z
				v2.push(x0, y0);
				//v2Dirt.push(v[i ]*scale, v[i +1]*scale);
			}
			
			
			sh.graphics.lineStyle(1, color);
			len = v2.length;
			for (i = 0; i < len; i += 2) {
				sh.graphics.drawCircle(v2[i], v2[i + 1], diam);
				
			}
			//(v2);
			return v2;
		}

		private function sliderChangeHandler(e:Event):void {
			var m3d:Matrix3D = new Matrix3D();
			var scale:Number = 180;
			var stP:Number = 3000;
			var v3d:Vector.<Number> = new Vector.<Number>();
			var focus:Number = 400;
			
			
			var scaleT:Number = 50;
			
			//m3d.appendTranslation( -0.5, 0.5, 0);
			m3d.appendTranslation( -0.5, sliderY.position * 3, 0);
			m3d.appendRotation( sliderX.position * 360, Vector3D.Z_AXIS);
			
			m3d.transformVectors(basUV3,v3d);
			
			var uv:Vector.<Number> = new Vector.<Number>();
			var i:int;
			
			for (i = 0; i < v3d.length; i += 3)
				uv.push(v3d[i], v3d[i + 1]);
			
			sh.graphics.clear();
			
			sh.graphics.beginBitmapFill(bmd);
			
			
			sh.graphics.drawTriangles(_scr, _tri, uv);
			
			
		}

		
	}

}

Компилим и видим вот-что:

Дергайте за слайдеры
Я себе гляжу на индикатор загрузки проца, он показывает 0, но стоит начать дергать за слайдеры проц сразу начинает загружаться от 30 до 60 процентов, ха... а еще надо физику прикрутить, конечно jiglibflash мне нравится, но и она сразу после включения скушает ок. 40 процентов проца, а еще надо модели отобразить деревья и прочее, не ну может можно прикрутить glaze 2д физику и игруля будет работать на 24 fps при 99% проца, но это не то, чего я хочу.
Исходничек если что потестите с разным
//кол-во сегментов
var amountSeg:int = 5;

Качество будет еще хуже если поставить 4, однако и загрузка на проц увеличится, если поставить 6, если поставить в плеере качество - низкое, то не будет заметно никакой разницы, но нагрузка на проц резко упадет и я думаю, что все-таки возможно на as3 сбацать приличную гоночку, тем более, что в той игре качество установлено в низкое.