November 2, 2009, 9:46 pm
AS3 Tank Game Tutorial Part 1
I've never posted a full tutorial for anything here before, but since I have a fascination for tank games (I LOVE TANK GAMES!!!1111one) I felt it only appropriate to post a tutorial with full code (for once ;) ). It isn't aimed at an advanced audience so code will be commented mercilessly and with intent to wound clarify.

Let's get started; our document class (if you are compiling in Flash CS3/4, you will need to set this first class as your document class! Those using mxmlc are assumed to know what to do) will simply create a new tank, add it to the stage, and add an event listener for ENTER_FRAME events to coordinate our game world:

package {	
	import flash.events.Event;	
	import flash.display.Sprite;	
	import flash.display.DisplayObject;	
	
	/**
	* @author tinyrobot
	*/
	
	//metadata tag that sets up our movie's stage properties
	[SWF(backgroundColor="#FFFFFF", frameRate="60", width="550", height="400")]
	
	public class TankGame extends Sprite {
		
		private var tank : Tank;
		
		/**
		 * Our constructor just creates a new tank, sets its position and adds it.
		 * It also sets up the timing mechanism for our game logic.
		 * Positioning the tank will eventually be the job of our World class (when
		 * we create one later!)
		 */
		public function TankGame() {
			tank = new Tank();
			tank.x = 50;
			tank.y = 50;
			addChild(tank);
			addEventListener(Event.ENTER_FRAME, update);
		}
		
		/**
		 * This method provides the synchronization of our game world: now it only
		 * tells one entity to update itself, but eventually it will synchronize
		 * all game objects.
		 */
		private function update(e : Event) : void{
			tank.update();
		}
	}
}

The comments I feel are fairly clear; we are using one update method to make sure that all game objects update themselves at a regular interval. This is the simplest method of timing, in future parts of this tutorial I will discuss attempts to create more accurate timing. I'm also not including package declarations properly since I'm going to cover that later in a topic on refactoring.

Now that's the basic entry point of our game out of the way; the fun part that will make the tank move follows in the Tank class:

package {
	import flash.geom.Matrix;
	import flash.display.DisplayObject;
	import flash.ui.Keyboard;
	import flash.events.KeyboardEvent;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.display.Sprite;

	/**
	 * @author tinyrobot
	 */
	public class Tank extends Sprite {

		/* a dictionary, mapping a keycode to a boolean. This allows us to handle
		   multiple keypresses. A much lazier way of doing Senocular's KeyObject:
		   http://www.senocular.com/flash/actionscript.php?file=ActionScript_3.0/com/senocular/utils/KeyObject.as */
		private var keys : Object = new Object();
		private var speed : Number = 0;
		private var rotationSpeed : Number = 2; //how much the tank will rotate by
		private var angle : Number = 0;
		private var tankView : DisplayObject; //our view

		public function Tank() {
			//we need an event listener so that we have a stage instance to listen
			//to keystrokes on!
			addEventListener(Event.ADDED_TO_STAGE, added);
			//load the tank image!
			var loader : Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, init);
			loader.load(new URLRequest("tank.png"));
		}
    
		private function init(e : Event) : void {
			tankView = e.target.loader as DisplayObject;
			addChild(tankView);
		}

		private function added(e : Event) : void {
			/*now we have a stage instance accessible we can add event listeners for the
		  keystrokes*/
			stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
		}

		private function onKeyDown(e : KeyboardEvent) : void {
			keys[e.keyCode] = true; //set the keycode to 'on' in our keys dictionary
		}

		private function onKeyUp(e : KeyboardEvent) : void {
			keys[e.keyCode] = false; //set the keycode to 'off' in our keys dictionary
		}

		public function update() : void {
			/* Check the relevant keys in the keys dictionary and act appropriately
			   if they are down! */
			if(keys[Keyboard.LEFT]) {
				angle -= rotationSpeed;
				rotate(-rotationSpeed);
			}
			if(keys[Keyboard.RIGHT]) {
				angle += rotationSpeed;
				rotate(rotationSpeed);
			}
			if(keys[Keyboard.UP]) {
				speed += 0.1;
			}
			if(keys[Keyboard.DOWN]) {
				speed -= 0.1;
			}
			
			//since our tank image points upwards, we need to take the sine of angle
			//and the -cosine of angle, since our start point looks like an L shape
			//instead of an r shape.
			//See http://pixwiki.bafsoft.com/mags/5/articles/circle/sincos.htm
			//for a decent explanation!
			this.x  = x + speed * Math.sin(angle * Math.PI / 180);
			this.y  = y + speed * -Math.cos(angle * Math.PI / 180);
			
		}
		
		/**
		 * This function takes an angle in degrees, and rotates our tank view to
		 * match it.
		 */
		private function rotate(ang : Number) : void{
			//get the transformation matrix of our view (all display objects have one!)
			var m : Matrix = tankView.transform.matrix;
			//translate our matrix left by half width and up by half height
			//this makes sure the origin of our rotation is the centre of our tank
			m.tx -= tankView.width/2;
			m.ty -= tankView.height/2;
			//rotate the matrix by the angle in radians
			m.rotate(ang * (Math.PI / 180));
			//translate the matrix back to its original position
			m.tx += tankView.width/2;
			m.ty += tankView.height/2;
			//set our view's matrix to apply the rotation to the view
			tankView.transform.matrix = m;
		}
	}
}

Again, the code is quite simple and probably quite self explanatory. Every update (called by the TankGame class, remember?) we will check to see if left, right, up or down are pressed. Left and right will alter our angle of movement, and up and down will alter the speed at which we travel in that direction. The setting of our x and y position just involves adding our speed to our angle of movement, such that our x and y position lie on a point at the apex of that angle. As in the comments, there's a good tutorial here.

Notice how this class contains quite a lot of logic to do with our tank. It checks for input, it responds to input, it modifies the position of the tank and it modifies the rotation of the 'view' of our tank. In a later installment I'll discuss better sepaaration of these things, which will help to make our game more flexible and therefore better equipped to cope with increased complexity as we go along.

That's all for this tutorial, all comments welcome! As an exercise, there's enough code here to add the ability for the Tank to fire a Bullet - see if you can extend this example to make your tank fire bullets! You can find the tank image (I'm no artist!) here.

2 comments. Add one!
September 18, 2009, 12:13 am
iPhone OS2 UIScrollView on OS3 devices
I noticed a bug in the iPhone SDK today when building for 2.1 devices and deploying on OS 3+ devices. It seems that in some circumstances (repeatedly scrolling very quickly) the scrollViewDidEndDecelerating method does not get called when willDecelerate is false in the scrollViewDidEndDragging method. When you deploy to 2.1 devices scrollViewDidEndDragging is always called, and when you build and deploy for 3+ devices it is also always called; so I figure it's an issue with the SDK. There's a simple workaround:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerating{
  if(!declerating){
    [self scrollViewDidEndDecelerating: scrollView];
  }
}

This works fine and is perfectly logical, it just stumped me because it is only required in a very specific scenario. Hope this is helpful to someone!
No comments. Add one!
June 17, 2009, 10:43 pm
RSS Feed and associated naughtinesses
RSS feed is now available for the last 10 posts! A brief php session and twas done. The site should also validate on pages that don't have code snippets as html strict, I still need to modify the post bodies to escape html chars.
No comments. Add one!
June 5, 2009, 5:11 pm
Animating a video with BitmapData
I want to create a sort of starfield using videos, so to start my experiments with animating video containers I thought I'd do this simple test:

video animation experiment

It just rotates a playing video that bounces around, but one could add animated scaling as well very easily. I'm surprised at the speed actually, we could reduce a bit of overhead by drawing directly to the background instead of an intermediate BitmapData instance (although the current method would allow filters etc. to be applied in addition to transformations before copying the whole lot to the main background BitmapData).

No comments. Add one!
June 5, 2009, 12:33 am
TinyRobot Preloader!
I decided I wanted to make a nice little preloader for my various things, which also doubled as an excuse to benchmark matrix scaling vs. an AS3 implementation of the nearest neighbor algorithm. The matrix version was far faster, and I used it for rotations too. I would quite like to get a rotozoom effect working too, but for now this will do:

Preloader

I would also quite like to see if I can get some other effects in there, although I appreciate that as internet speeds become faster the humble preloader shall eventually wane :( It's a shame really, with the speed of the new flash 10 with typed arrays old-skool demo effects are becoming more plausible, which would be perfect for cool preloaders.

No comments. Add one!
June 4, 2009, 4:26 pm
Polychromatic Space Unicorn Death Squad
Released!

Proof, if any were required, that a funny idea does not necessarily translate into a funny or even fun game. Last as long as you can against the space unicorn death squad invasion fleet! Face many powerful enemy weapons with your trusty peashooter! Die like the last puny human you are!

No comments. Add one!
May 28, 2009, 1:44 pm
polychromatic space unicorn death squad dev update one
Luckily SQLite just has a text type so my post titles can be infuriatingly long... mwahahaha!

So after a holiday and being busy and going to hospital for tonsillitis, I remembered my website still exists! Before I got sick I decided it would be funny to make a game based on a mural I saw on a garage in San Franciso:

inspirado

So I thought it would be funny, knocked up a little music in milkytracker, found a cow sound effect (because obviously unicorns go moo) and went at it. Initially I thought it would be fun to just see if I could do the whole thing from scratch in 2 hours, but sadly my effort fell short - everything was sort of there, but there was no real game to play! The problem is, I initially wanted to do it just as a joke really; but as I thought more about it (while sick) I thought it would be quite fun to have a space invaders clone that wasn't necessarily to formulaic - specifically with regards to enemy movement. My current ambition is to implement different enemy behaviours, so that the game has a more organic feel - I don't actually want the game to be 'winnable' necessarily; alien invaders by virtue of actually managing to get here would be unlikely to be thwarted by a single tenacious human adversary!

Instead I think it would be sensible to implement a finite state machine for the unicorns, and alter that as gameplay progresses to maintain the player's interest. Naturally difficulty should progress as a function of time, although I think it would be cool to alter the nature of the difficulty (not just making enemies faster, for example). So the premise of this game is space invaders, with novel enemy behaviours - kind of like... well... galaga really. But with polychromatic unicorns! Anyway, it'll be fun to program the different behaviours, which is the main point of these things for me ;)

2 comments. Add one!
April 27, 2009, 11:23 pm
More on tanks and fullscreen and versus mode!
I decided after I cobbled together an initial 'story mode' for the tanks game I posted awhile ago that I'd like to use the game as a means to learn as much as possible about 2d game development in general. I then decided it'd be nice to have other game modes; I implemented 'arcade mode' first as it required almost no changes to story mode, save for making the player start at level one again whenever they died.

I also wanted a versus mode, harking back to the days of two players on one keyboard - I stumbled into a key ghosting issue, which I eventually solved by changing my control scheme to use control and shift for fire - I'm not sure it'll solve the problem for everyone, but it's better than nothing! I wrote a small ControlScheme class in the process, which has gone back into what is becoming a sort of Slick partial reimplementation in as3.

I also had to solve the split screen issue, which works perfectly now and I'm very happy with, since it's given me a nice reusable Camera class :D I've still got to refactor collision detection, although I've started. I'm also planning a two player coop mode, a bot deathmatch mode, and a two player bot deathmatch mode. There may also be network play at some point in the future, but that's a whole other kettle of fish...

The last thing I did was investigate fullscreen in flash, which is sort of ok as long as you don't want people to use it in their browsers - with the latest flash player you can use nonprintable characters like arrow keys and space, but the only reason I want to use fullscreen is for two player split screen, which needs to use printable characters...

Also had to use fscommand to trap all keys in the standalone player, otherwise ctrl (fire for player 2) and w (forward for player 1) will close the movie :) I also had to resize things to suit the user's monitor aspect ratio, but that works fine. I'm planning eventually to release the whole game as an offline game, so fullscreen can be exploited usefully.

The only thing left that slightly bugs me is that I have to fully qualify dynamic class names when I attach them to tiles in Tiled, but I suppose that's not too irritating :D And I've begun rewriting the bot AI to utilize some ideas from Reynold's paper. It's working ok so far, but I haven't combined it with the A* implementation I'm using yet - currently the bots are very dumb and wasteful, and will plot a path to the player even if they aren't on screen! So ideally we'll have a random wandering behaviour and a hunting behaviour: the bots could certainly use A* as a subsidiary hunting method, but I may just ditch it and only hunt if there is line of sight... I think the current behaviour is a bit 'zombie swarm' for my liking, and following the A* path tile for tile looks very unnatural. Turrets and Boss tanks are good so far - the Boss tanks move so slowly that their tracking looks fine, and the player will almost definately die before the tank ends up on top of them and their dumbness becomes apparent. I've also got some other game ideas after this is done, but hopefully if I concentrate on this project I'll be able to get the subsequent ones completed far more quickly :)

No comments. Add one!
April 26, 2009, 10:24 pm
2d Tile engine Camera in AS3 redux
As a follow up to my least effort solution, here is a demo showing the solution that I actually needed all along, with multiple cameras blitting to just a single bitmap data. Multiple BitmapDatas (and Bitmaps) could be convenient from a strictly AS3 viewpoint, but that would make the code itself unportable. The current code *is* portable, and I'm going to clean it up and then post it when I'm back from Vegas.

I did have a brief problem because I was trying to render subpixels which is of course silly. And when I say brief, I mean I've had essentially working code for fricking ages and it took me way too long to realize that I was trying to render fractional pixels using the COPY !*$ing PIXELS method of BitmapData. Ah well, it's working now so I can finally implement two player split screen in SUPERAWESOMETANKSSSSSSS!!111!!111111oneone.

Oooh, DEMO - should be visually exactly the same as previous, but with just the one BitmapData.

*Edit* - slightly different, does a fill of white so we don't get nasty "smudging" :)

No comments. Add one!
April 25, 2009, 4:29 pm
2d Camera test in as3
Sooo, I figured I would like to add split screen mode to SUPERAWESOMETANKS (that never gets old for me :D) for some good old fashioned eye straining finger bending two-players-on-one-keyboard style two player action. Fest.

After pissing about for ages with offsets and one main bitmapdata (it's calculating the tile scroll clip that's the irritating thing) I figured adding multiple bitmaps to the screen might not be too bad; after all the total memory required should be the same (ish) for one bitmapdata at 512x384 as two at 256x192 since the bulk of the memory requirement should be for the pixel matrix. So I've decided to modify my framework slightly and render everything through cameras to make it more manageable.

This is what I've ended up with

Sauce when it's done. I need to set svn up somewhere actually.

No comments. Add one!