Intel sponsors gamedev.net search:   
Tesseract's Game Development JournalBy Tesseract      

How I Work
Preferred Language
Actionscript 3
Development Environment
Notepad++ for coding
Flex 3 SDK for compiling
Graphics
The GIMP, among others

See my personal weblog

Completed (and published) Flash Game Projects:
Games
TriGavoid
Tutorials
Creating Textures with Perlin Noise
Simple Trigonometry and Curves Tutorial



Thursday, June 28, 2007
Screenshot of Parallax Sprite experiment

Click the image to check this one out. I spent an hour or so modifying my code to place bitmaps in the experiment, and another hour figuring out how to make simple pixel-ish images in The Gimp. The problem with The Gimp is that I automatically expect it will be less user-friendly than it is, just based on reputation. It is actually quite intuitive, from a developer/nerd perspective.

Anyway, I have made the engine a little more robust. Now instead of drawing each ParallaxSprite to the display container one at a time, I cycle through the ParallaxSprites and attach the bottom-most layer from each, then cycle through again and add the next layer, and again and so on until all layers of each sprite have been drawn. This allows for "meshing" of objects which overlap, instead of one being drawn completely on top of another. Since the position of each of the display objects is controlled by its respective class, I can still move everything around without any performance loss over rendering each ParallaxSprite individually.

I suppose the next step is having the ground scroll along with everything else. I would like to have this work as a real movement through space, and not simply wrapping the tiles/sprites, as is currently happening with the trees.

Anywho, here are the updated classes:

World.as
package {
	import flash.display.*;
	import flash.events.*
	[SWF(backgroundColor="0x000000",width="640",height="640",frameRate="50")]
	public class World extends Sprite {
		private var sprites:Array = [];
		private var totalSprites:Number = 30;
		private var maxHeight:Number = 0;
		public var centerX:Number;
		public var centerY:Number;
		
		private var dX:Number;
		private var dY:Number;
		private var dOff:Number;
		private var velocity:Number;
		private var theta:Number;
		
		[Embed("ground.jpg")]
		private var Ground:Class;
		
		public function World() {
			addEventListener(Event.ENTER_FRAME,init);
		}
		
		private function init(e:Event):void {
			if(!stage) return;
			removeEventListener(Event.ENTER_FRAME,init);
			centerX = stage.stageWidth/2;
			centerY = stage.stageHeight/2;
			
			var g:Bitmap = new Ground();
			var b:BitmapData = new BitmapData(g.width,g.height);
			b.draw(g);
			graphics.beginBitmapFill(b);
			graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);
			graphics.endFill();
			
			graphics.beginFill(0xff0000,100);
			graphics.drawCircle(centerX,centerY,2.5);
			graphics.endFill();
			for(var i:Number = 0;i<totalSprites;i++) {
				sprites[i] = new ParallaxSprite(
					this,
					Math.round(Math.random()*stage.stageWidth/100)*100,
					Math.round(Math.random()*stage.stageHeight/100)*100
				);
				sprites[i].cacheAsBitmap = true;
				maxHeight = Math.max(sprites[i].layers,maxHeight);
			}
			
			for(i = 0;i<maxHeight;i++) {
				for(var j:Number = 0;j<totalSprites;j++) {
					if(sprites[j].spriteLayers[i]) {
						addChild(sprites[j].spriteLayers[i]);
						sprites[j].spriteLayers[i].cacheAsBitmap = true;
					}
				}
			}
			addEventListener(Event.ENTER_FRAME,moveSprites);
		}
		
		private function moveSprites(e:Event):void {
			dX = centerX - mouseX;
			dY = centerY - mouseY;
			theta = Math.atan2(dY,dX);
			dOff = Math.sqrt(dX*dX+dY*dY);
			velocity = dOff/50;
			for(var i:Number = 0;i<totalSprites;i++) {
				sprites[i].x += Math.cos(theta)*velocity;
				sprites[i].y += Math.sin(theta)*velocity;
				if(sprites[i].x < -30) sprites[i].x = stage.stageWidth + 30;
				else if(sprites[i].x > stage.stageWidth+30) sprites[i].x = -30;
				if(sprites[i].y < -30) sprites[i].y = stage.stageHeight + 30;
				else if(sprites[i].y > stage.stageHeight+30) sprites[i].y = -30;
				sprites[i].detectCenter();
			}
		}
	}
}


ParallaxSprite.as
package {
	import flash.display.*

	public class ParallaxSprite extends Sprite {
		public var layers:Number;
		private var offsetRatio:Number = 1.05;
		private var sizeOffset:Number = 1;
		private var maxCenterDistance:Number;
		private var world:World;
		private var dX:Number = 0;
		private var dY:Number = 0;
		private var d:Number = 0;
		private var r:Number = 0;
		public var spriteLayers:Array = new Array();
		
		/*	EMBEDDED ASSETS	*/
		[Embed("tree_layer_0.png")]
		private var Tree_0:Class;
		[Embed("tree_layer_1.png")]
		private var Tree_1:Class;
		[Embed("tree_layer_2.png")]
		private var Tree_2:Class;
		[Embed("tree_layer_3.png")]
		private var Tree_3:Class;
		
		public function ParallaxSprite($world:World,$x:Number,$y:Number) {
			world = $world;
			x = $x;
			y = $y;
			maxCenterDistance = Math.min(world.centerX,world.centerY)/2
			init();
		}

		private function init():void {
			layers = 23;
			var rW:Number = Math.round(Math.random()*25)+25;
			var rH:Number = Math.round(Math.random()*30)+20;
			var rC:Number = Math.round(Math.random()*10)*0x111111;
			for(var i:Number=0;i<20;i++) {
				spriteLayers[i] = new Tree_0();
			}
			spriteLayers[20] = new Tree_1();
			spriteLayers[21] = new Tree_2();
			spriteLayers[22] = new Tree_3();
			for(i=0;i<22;i++) {
				spriteLayers[i].x = x-50;
				spriteLayers[i].y = x-50;
				spriteLayers[i].cacheAsBitmap=true;
			}
		}
		
		public function detectCenter():void {
			dX = x - world.centerX;
			dY = y - world.centerY;
			d = Math.sqrt(dX*dX+dY*dY);
			var o:Number = d/maxCenterDistance*offsetRatio;
			r = Math.atan2(dY,dX);
			for(var i:Number = 0;i<spriteLayers.length;i++) {
				spriteLayers[i].x = x+i*o*Math.cos(r)-50;
				spriteLayers[i].y = y+i*o*Math.sin(r)-50;
			}
		}

	}
}


Comments: 0 - Leave a Comment

Link



Wednesday, June 27, 2007
I just posted another experiment. This one is the beginnings of a parallax sprite engine, inspired by the Dixie Chicks Underground website created by WeFail. Seems easy enough so far, although I have done nothing but throw up some layered graphics.

You will need the Flash 9 plugin to see this one.

Here is the rough draft of the ParallaxSprite class:
package {
	import flash.display.*

	public class ParallaxSprite extends Sprite {
		private var layers:Number;
		private var offsetRatio:Number = 1.01;
		private var sizeOffset:Number = 1.1;
		private var maxCenterDistance:Number;
		private var world:World;
		private var dX:Number = 0;
		private var dY:Number = 0;
		private var d:Number = 0;
		private var r:Number = 0;
		private var spriteLayers:Array = new Array();
		public function ParallaxSprite($world:World,$x:Number,$y:Number) {
			world = $world;
			x = $x;
			y = $y;
			maxCenterDistance = Math.min(world.centerX,world.centerY)/2
			init();
		}
		
		private function init():void {
			layers = Math.round(Math.random()*30)+30;
			var rW:Number = Math.round(Math.random()*25)+25;
			var rH:Number = Math.round(Math.random()*30)+20;
			var rC:Number = Math.round(Math.random()*10)*0x111111;
			for(var i:Number = 0;i<layers;i++) {
				spriteLayers[i] = new Shape();
				if(i%10==0 || i==layers-1)	spriteLayers[i].graphics.lineStyle(1,0xededed,100);
				spriteLayers[i].graphics.beginFill(rC,100);
				spriteLayers[i].graphics.drawRect(-rW/2 * sizeOffset,-rH/2 * sizeOffset,rW * sizeOffset,rH * sizeOffset);
				spriteLayers[i].graphics.endFill();
				spriteLayers[i].x = i;
				spriteLayers[i].y = i;
				spriteLayers[i].cacheAsBitmap=true;
				addChild(spriteLayers[i]);
				sizeOffset *= offsetRatio;
			}
		}
		
		public function detectCenter():void {
			dX = x - world.centerX;
			dY = y - world.centerY;
			d = Math.sqrt(dX*dX+dY*dY);
			var o:Number = d/maxCenterDistance*offsetRatio;
			r = Math.atan2(dY,dX);
			for(var i:Number = 0;i<spriteLayers.length;i++) {
				spriteLayers[i].x = i*o*Math.cos(r);
				spriteLayers[i].y = i*o*Math.sin(r);
			}
		}

	}
}


And here is the World class which creates and powers the ParallaxSprites:

package {
	import flash.display.Sprite;
	import flash.events.*
	[SWF(backgroundColor="0x000000",width="640",height="640",frameRate="50")]
	public class World extends Sprite {
		private var sprites:Array = [];
		private var totalSprites:Number = 20;
		public var centerX:Number;
		public var centerY:Number;
		public function World() {
			addEventListener(Event.ENTER_FRAME,init);
		}
		
		private function init(e:Event):void {
			if(!stage) return;
			removeEventListener(Event.ENTER_FRAME,init);
			centerX = stage.stageWidth/2;
			centerY = stage.stageHeight/2;
			graphics.beginFill(0xff0000,100);
			graphics.drawCircle(centerX,centerY,5);
			graphics.endFill();
			for(var i:Number = 0;i<totalSprites;i++) {
				sprites[i] = new ParallaxSprite(
					this,
					Math.round(Math.random()*stage.stageWidth/50)*50,
					Math.round(Math.random()*stage.stageHeight/20)*20
				);
				sprites[i].cacheAsBitmap = true;
				addChild(sprites[i]);
			}
			addEventListener(Event.ENTER_FRAME,moveSprites);
		}
		
		private function moveSprites(e:Event):void {
			for(var i:Number = 0;i<totalSprites;i++) {
				sprites[i].x--;
				if(sprites[i].x < -30) {
					sprites[i].x = stage.stageWidth + 30;
					sprites[i].y = Math.round(Math.random()*stage.stageHeight-40)+20;
				}
				sprites[i].detectCenter();
			}
		}
	}
}


Comments: 0 - Leave a Comment

Link



Tuesday, June 26, 2007
So I did a Google search for "game mechanics" - a phrase which totally escaped me in the past - and on the first page I discovered a site called 300, wherein the author is attempting to come up with a new game mechanic every day for 30 days. So far he is up to 48. He has included screenshots of all of the games, and a lot of them - particularly "Pacman as an RPG" look pretty cool!

I imagine he is probably a user here a gd.net.



Comments: 0 - Leave a Comment

Link



Saturday, June 23, 2007
I just finished playing around with the blur filter, combined with double buffering, in Actionscript 3. You can see the result here.

The class I am using is as follows:

package{
	import flash.display.*;
	import flash.events.*;
	import flash.ui.Keyboard;
	import flash.geom.*;
	import flash.filters.BlurFilter;
	[SWF(backgroundColor="0x000000",width="540",height="540",frameRate="50")]
	public class KeyCodes extends Sprite {
		/*					*/
		/*	EMBEDDED ASSETS	*/
		/*					*/
		
		[Embed(source="ship_1.gif")]
		private var Ship:Class;
		private var ship:Bitmap;
		
		private var ball:Sprite;
		private var flame:Sprite;
		private var _bitmap:BitmapData;
		private var _buffer:BitmapData;
		private var _buffer_clear:BitmapData;	//	used to "clear" the bitmapdata
		private var _blur_layer:BitmapData;
		private var _image:Bitmap;
		private var _blurFilter:BlurFilter;
		
		private var SPEED:Number = Math.PI*2/100;	//	number of steps around a circle
		private var THETA:Number = Math.PI*.5;
		private var RAD_DEG:Number = 180/Math.PI;
		private var radius:Number;
		private var flameRadius:Number;
		private var centerX:Number;
		private var centerY:Number;
		private var angle:Number;
		
		private var xDirection:Number = 0;
		public function KeyCodes() {
			_blurFilter = new BlurFilter();
			addEventListener(Event.ENTER_FRAME,init);
		}
		private function init(e:Event):void {
			if(!stage) return;
			removeEventListener(Event.ENTER_FRAME,init);
			ball = new Sprite();
			ship = new Ship();
			ship.x = -(ship.width/2);
			ship.y = -(ship.height/2);
			ship.rotation = 90;
			ball.addChild(ship);
			
			flame = new Sprite();
			flame.graphics.beginFill(0x990000,100);
			flame.graphics.drawCircle(4,-10,4);
			flame.graphics.endFill();
			flame.graphics.beginFill(0x990000,100);
			flame.graphics.drawCircle(4,10,4);
			flame.graphics.endFill();
			
			centerX = stage.stageWidth/2;
			centerY = stage.stageHeight/2;
			radius = stage.stageWidth*.3;
			flameRadius = radius + (ship.height*1.5);
			ball.x = centerX + radius*Math.cos(THETA);
			ball.y = centerY + radius*Math.sin(THETA);
			flame.x = centerX + flameRadius*Math.cos(THETA);
			flame.y = centerY + flameRadius*Math.sin(THETA);
			_bitmap = new BitmapData(stage.stageWidth,stage.stageHeight,true,0xff000000);
			_buffer = new BitmapData(stage.stageWidth,stage.stageHeight,true,0x00000000);
			_buffer_clear = new BitmapData(stage.stageWidth,stage.stageHeight,true,0xff000000);
			_blur_layer = new BitmapData(stage.stageWidth,stage.stageHeight,true,0xff000000);
			_image = new Bitmap(_bitmap);
			addChild(_image);
			
			stage.addEventListener(KeyboardEvent.KEY_DOWN,setDirection);
			stage.addEventListener(KeyboardEvent.KEY_UP,unsetDirection);
			addEventListener(Event.ENTER_FRAME,moveMe);
		}
		public function moveMe(e:Event):void {
			THETA += SPEED * xDirection;
			ball.x = centerX + radius*Math.cos(THETA);
			ball.y = centerY + radius*Math.sin(THETA);
			flame.x = centerX + flameRadius*Math.cos(THETA);
			flame.y = centerY + flameRadius*Math.sin(THETA);
			var r:Number = Math.atan2(centerY - ball.y , centerX - ball.x)*RAD_DEG;
			ball.rotation = r;
			flame.rotation = r;
		//	_buffer.draw(_buffer_clear);
			_blur_layer.draw(flame,flame.transform.matrix);
			_blur_layer.applyFilter(_blur_layer,_blur_layer.rect,new Point(),_blurFilter);
			_buffer.draw(_blur_layer);
			_buffer.draw(ball,ball.transform.matrix);
			_bitmap.draw(_buffer);
		}
		
		public function setDirection(e:KeyboardEvent):void {
			switch(e.keyCode) {

				case Keyboard.LEFT :
					xDirection = -1;
					break;
				case Keyboard.RIGHT :
					xDirection = 1;
					break;
				default:
					break;
			}
			
		}
		public function unsetDirection(e:KeyboardEvent):void {
			xDirection = 0;
		}
	}
}



Every day, I am a little more comfortable with the new version of Actionscript. Conceptually, it is easier to grasp than was AS2, and of course it is much cleaner code than AS1.

I still have a few details to iron out with the keyboard events; they are more precise than I am used to.

As a side note, today I figured out how to change the default display for the (ugly to the point of being unusable) command prompt in Windows: launch cmd.exe, then right-click anywhere on the window chrome, and select "defaults". Now I have a 1000px wide window with a black background displaying 12-point green lucida console text. Much easier to read than that gray VIC-20 - esque default style.

Comments: 2 - Leave a Comment

Link



Thursday, June 21, 2007
I have just figured out how to implement Double Buffering in Actionscript 3. Here is the code for a recent experiment:

package {
 import flash.display.*;
 import flash.events.Event;
 import flash.geom.*;
 import flash.filters.BlurFilter;
 /*	metadata for using mxmlc commaond-line compiler	*/
 [SWF(backgroundColor="0x000000",width="640",height="640",frameRate="50")]
  public class PlasmaGhost extends Sprite {
  private var _graphics:Array = new Array();
  private var _bitmap:BitmapData;			 		
  private var _buffer:BitmapData;
  private var _image:Bitmap;								
  private var _blurFilter:BlurFilter;
  private var centerX:Number;
  private var centerY:Number;
  private var c:Number = 4;
  private var d:Number = 3;
  private var radius:Number = 50;  //	radius of circle
  private var theta:Number = 0;	//	how far we are around the circle
  private var steps:Number = 540;  //	iterations to complete a revolution
  private var stepSize:Number = Math.PI*2/steps;  //	actual movement in each iteration
  private var particles:Number = 500;  //	number of particles in display list
  private var placement:Number = Math.PI*2/particles;	//	relative placement of each particle
  private var copyRect:Rectangle;
  private var copyPoint:Point;
  public function PlasmaGhost() {
    for(var i:Number=0;i&lt;particles;i++) {
      _graphics[i] = new Shape();
      _graphics[i].graphics.beginFill(0x040404*(i%64),100);
      _graphics[i].graphics.drawCircle(0,0,1);
      _graphics[i].graphics.endFill();
    }
    _blurFilter = new BlurFilter();
    addEventListener(Event.ENTER_FRAME,registerStage);
  }
  public function registerStage(event:Event):void {
    if(stage) {
      removeEventListener(Event.ENTER_FRAME,registerStage);
      copyPoint = new Point(0, 0);
      centerX = stage.stageWidth/2;
      centerY = stage.stageHeight/2;
      radius = Math.min(stage.stageWidth,stage.stageHeight)*.8/2;
      for(var i:Number=0;i&lt;particles;i++) {
        _graphics[i].x = Math.random()*stage.stageWidth;
        _graphics[i].y = Math.random()*stage.stageHeight;
      }
      _bitmap = new BitmapData(stage.stageWidth,stage.stageHeight,true,0xff000000);
      _buffer = new BitmapData(stage.stageWidth,stage.stageHeight,true,0xff000000);
      _image = new Bitmap(_bitmap);
      addChild(_image);
      addEventListener(Event.ENTER_FRAME,onEnterFrame);
    }
  }
  private function onEnterFrame(event:Event):void {
    for(var i:Number=0;i&lt;particles;i++) {
      _graphics[i].x = centerX + radius * (Math.cos(c/d*(theta * placement*i)) * Math.cos(theta + placement*i));
      _graphics[i].y = centerY + radius * (Math.cos(c/d*(theta + placement*i)) * Math.sin(theta * placement*i));
      _buffer.draw(_graphics[i],_graphics[i].transform.matrix);
    }
    _buffer.applyFilter(_buffer,_buffer.rect,new Point(),_blurFilter);
    _bitmap.draw(_buffer);
    theta +=stepSize;
  }
}
}



There are three important parts to this: _buffer, _bitmap, and _image.

When the program loads, _bitmap is set to be the data source for _image. _image is the actual visible object on the screen. _buffer is used to hold the graphics in the array called _graphics as they are moved and re-drawn. At every iteration, each element in _graphics is updated according to the formula for the pattern, and then drawn to _buffer. Once _graphics has been iterated through, _buffer is copied onto _bitmap. Since _bitmap is the data source for _image, whatever is in _bitmap appears on the screen.

Some additional notes about this piece of code:

1. at the beginning there is a line of meta-data which is used to set the Stage width, height, background color, and frame rate. I use this line because I am using the MXMLC.exe command-line compiler - included with the free Flex SDK - to compile my code, so I can't set these parameters manually in the Flash CS3 IDE. If you are using the Flash CS3 IDE, you can remove this line.

2. In the constructor function there I call an event which, in effect, checks to see if the stage (base container for all displayed objects) has instantiated yet. I had to do this because I am finding that, at higher frame rates, the code executes and tries to begin drawing before the stage has instantiated. It is a minor bug fix, and useful to know in case the situation arises.

3. The formula I use to create the pattern is a variation on the Rose.

For more information about using the command-line compiler, see Senocular's tutorial.

Comments: 0 - Leave a Comment

Link



Thursday, June 7, 2007
This is an idea I have been kicking around for a long time. You have to fill and maintain a seed bank in the face of the apocalypse.

For whatever reason, civilization as we know it is about to come to an end. You know it, you see it coming, and you try to warn people, but no-one will listen. So you take matters into your own hands.

The game will be broken into two parts: Pre and post- apocalypse. In the first half, you must go around the world | land | continent and gather seeds and nuts to stockpile for re-planting after the world ends. You must find a minimum variety of seeds, and a minimum quantity of each species.

Cue the apocalypse. You go inside and spend an entertaining x number of months/years protecting the seeds from those who would eat them...or you.

Finally the apocalypse is over. Time to go out into the barren wasteland and try to re-start the ecology. With luck, you still have enough seeds to help civilization get back on its feet.

There are many details I could throw in, like a time limit, being the countdown to the end of the world, then another countdown once you re-emerge, trying to get the first crop in before it is too late in the season, or before you starve.

Special treasures could include various kinds of fertilizers, or hydroponics equipment, or water reclamation facilities. The first half of the game could include simple economics as you try to fill out your collection by trading with the people around you.

I would want a lot of the content and landscape to be procedurally generated, just to keep it from being the same game every time. With the research I did for the game last year, it shouldn't be too difficult to set up a system of some kind.

Comments: 0 - Leave a Comment

Link



Wednesday, June 6, 2007
...he says as he eagerly awaits the announcement of 4e6.

I have spent the past couple of months working my tail off. No time for extracurricular programming. Last week I dove into Actionscript 3, and this week I have most of a web service enabled, SOAP-using, kiosk-based, XML-driven application built. If the project doesn't crash and burn in the next two weeks, it should be out in the world by the end of the month.

Coincidentally, that seems to be roughly when 4e6 will begin. Superpig has said things are running a little late this year, so the deadline for completion may be extended into the Christmas holidays. Fine with me. I just want to start making a game again.

And maybe get a little farther than I did last year. 4e5 was, for me, completely derailed by (a) a new girlfriend, (b) a new job, and (c) a dead computer. I had a lot of notes, and quite a few disconnected class files, but nothing even remotely resembling a playable demo, or even a start screen. Perhaps this year I will be a little more disciplined with my time.

In the meantime, I will use this forum to post game ideas, in the hopes that I or someone who reads one of them will find the ideas of some use. And if I ever get around to programming a game again, I will post it here.

Anything I do will be in Flash 9, either browser-based or a standalone executable.

Comments: 0 - Leave a Comment

Link


All times are ET (US)

 
S
M
T
W
T
F
S
1
2
3
4
5
8
9
10
11
12
13
14
15
16
17
18
19
20
22
24
25
29

OPTIONS
Track this Journal

 RSS 

ARCHIVES
November, 2009
March, 2009
January, 2009
November, 2008
October, 2008
September, 2008
August, 2008
July, 2008
February, 2008
December, 2007
November, 2007
October, 2007
September, 2007
July, 2007
June, 2007
March, 2007
February, 2007
January, 2007
December, 2006
August, 2006
July, 2006
June, 2006