Hyperspace starfield

Recently I encountered one of those timed programming-tests they occasionally do as part of an interview-process. Even though my solution was deemed a success, I personally felt somewhat dissatisfied with my approach to the presented problem.
Sure, the time available was severely limited, but I couldn’t help but feel frustrated by the under-the-hood clunkiness of the end-result, so I decided to redo it the “proper” way for the sake of my inner tranquility, and for the benefit of all mankind. :D

Lo and behold this starfield-effect:

Unlike the original submitted for the above-mentioned test, this one I actually consider to be a pretty solid version.
Even though the framerate of this SWF is capped at 30fps and the number of stars limited to 1000 for the sake of usability, on my (not particularly cutting-edge) system it still runs at a glossy 60fps with 1500 stars.

I even added little performance-leeching extra features, like the cool “redshift” trail-effect, or the elliptical movement of the stars’ point of origin.

And now onto the code. One noteworthy detail is the heavy-duty use of Flash10′s new Vector class – essentially a fast, typed array – which contributes to the nice performance-results:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Hyperspace starfield  (C) edvardtoth.com
 
package {
 
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.display.StageScaleMode;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.PixelSnapping;
	import flash.filters.BlurFilter;
	import flash.filters.ColorMatrixFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.events.Event;
 
	public class Hyperspace extends MovieClip
	{
		private var starNum:int;
		private var starSpeed:Number;
 
		private var displayWidth:Number;
		private var displayHeight:Number;
		private var halfDisplayWidth:Number;
		private var halfDisplayHeight:Number;
 
		private var circleWidth:Number;
		private var circleHeight:Number;
		private var circleAngle:Number;
		private var circlePoint:Point;
 
		private var currentStar:Star;
		private var starField:Vector.<Star>; 
		private var starHolder:Sprite;
 
		private var canvasBitmapData:BitmapData;
		private var canvasBitmap:Bitmap;
		private var canvasBlur:BlurFilter;
		private var canvasFade:ColorMatrixFilter;
 
		private var clipRect:Rectangle;
		private var point:Point;
 
		public function Hyperspace():void 
		{
			if ( stage != null )
			{
				stage.scaleMode = StageScaleMode.NO_SCALE;
				stage.align = StageAlign.TOP_LEFT;
				stage.quality = StageQuality.MEDIUM;
 
				displayWidth = stage.stageWidth;
				displayHeight = stage.stageHeight;
			}		
 
			halfDisplayWidth = displayWidth >> 1;
			halfDisplayHeight = displayHeight >> 1;
 
			circleWidth = displayWidth >> 2;
			circleHeight = displayHeight >> 2;
 
			circleAngle = 0;
			circlePoint = new Point (0, 0);
 
			clipRect = new Rectangle (0, 0, displayWidth, displayHeight);
			point = new Point (0, 0);
 
			starHolder = new Sprite();
			starField = new Vector.<Star>; // the stars are stored in this Vector (typed array)
			starNum = 1000; // the number of stars
			starSpeed = 1.2;
 
			var canvasFadeMatrix:Array = new Array();
 
			canvasFadeMatrix = canvasFadeMatrix.concat([1, 0, 0, 0, 0]); 	// red
			canvasFadeMatrix = canvasFadeMatrix.concat([0, 0, 0, 0, 0]); 	// green
			canvasFadeMatrix = canvasFadeMatrix.concat([0, 0, 0, 0, 0]); 	// blue
			canvasFadeMatrix = canvasFadeMatrix.concat([0, 0, 0, 0.97, 0]); // alpha
 
			canvasFade = new ColorMatrixFilter (canvasFadeMatrix);
			canvasBlur = new BlurFilter (8, 8, 1);
 
			canvasBitmapData = new BitmapData (displayWidth, displayHeight, true, 0x00000000);
			canvasBitmap = new Bitmap (canvasBitmapData, PixelSnapping.AUTO, false);
 
			addChildAt (canvasBitmap, 0);
 
			// create the initial set of stars
			for (var i:int = 0; i < starNum; i++)
			{
				currentStar = new Star();
 
				currentStar.xPos = Math.random() * 50.0 - 25.0;
				currentStar.yPos = Math.random() * 50.0 - 25.0;
				currentStar.zPos = Math.random() * 100.0;
 
				starHolder.addChild (currentStar);
 
				starField[i] = currentStar;
			}
 
			addEventListener(Event.ENTER_FRAME, updateFrame, false, 0, true);
		}
 
		private function updateFrame(event:Event):void 
		{
			// the stars' point of origin is slowly moving around on a elliptical path
			// this is where the points of the path are calculated / updated
 
			circlePoint.x = halfDisplayWidth + Math.cos (circleAngle) * circleWidth;
			circlePoint.y = halfDisplayHeight + Math.sin (circleAngle) * circleHeight;
			circleAngle += 0.02;
 
			var i:int = -1; // to make sure the first element in the while-loop is handled properly
 
			while (++i < starNum)
			{
				currentStar = starField[i];
 
				// if a star reaches or goes past the camera-plane, 
				// it's assigned new random coordinates and pushed back 
				if (currentStar.zPos <= 0.0) 
				{ 
					currentStar.xPos = Math.random() * 50 - 25;
					currentStar.yPos = Math.random() * 50 - 25;
					currentStar.zPos = 100.0;
				}
 
				currentStar.zPos -= starSpeed;
				currentStar.x = currentStar.xPos / currentStar.zPos * displayWidth + circlePoint.x;
				currentStar.y = currentStar.yPos / currentStar.zPos * displayHeight + circlePoint.y;
 
				// the proper scale and transparency of each star is set here
				currentStar.scaleX = currentStar.scaleY = currentStar.alpha = 1.0 - currentStar.zPos * 0.01;
			}
 
			// the on-screen bitmap buffer is processed here, and the 
			// starHolder container is drawn into it every frame as well.
			// besides creating the "redshift" trail-effect this also makes it 
			// unnecessary to check for out-of-bounds stars in order to reduce off-screen draw.
			// (try "Show Redraw Regions" to verify)
 
			canvasBitmapData.applyFilter (canvasBitmapData, clipRect, point, canvasBlur);
			canvasBitmapData.applyFilter (canvasBitmapData, clipRect, point, canvasFade);
			canvasBitmapData.draw (starHolder, starHolder.transform.matrix, null, null, clipRect, false);
		}
 
	}
 
}

…and here’s the additional little class which is used for each individual star:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// (C) edvardtoth.com
 
package {
 
	import flash.display.Sprite;
 
	public class Star extends Sprite
	{
                // yes, the lack of getters and setters is bad manners,
                // but for this particular application it's also way faster 
		public var xPos:Number;
		public var yPos:Number;
		public var zPos:Number;
 
		public function Star()
		{
			xPos = 0;
			yPos = 0;
			zPos = 0;
 
			graphics.beginFill (0xffffff);
			graphics.drawCircle (0.0, 0.0, 3.0);
			graphics.endFill();
		}
 
	}
 
}
3 Responses to Hyperspace starfield
  1. Kathleen
    April 2, 2010 | 12:52 pm

    I’m just starting to learn AS after animating time line in Flash for years.Thanks for all the cool stuff you are posting

  2. Og2t
    June 7, 2010 | 1:12 am

    I like the red trails a lot! Thanks for posting.

  3. zizi
    February 25, 2012 | 4:28 am

    Hi bro..Im sucks at AS…could email me the .fla
    Your work is a total madman genius…

    Please email to : efxtive@gmail.com

    thanks a bunch bro…i love u

Leave a Reply

Headway Themes — The Drag & Drop WordPress Theme