logo
468x60-2-495


  • Home
  • Privacy Policy
  • About
search
Nov 17, 2011 Posted on Nov 17, 2011 in Hints and Tips | 10 comments

An Introduction to FlashPunk: Creating a Spaceship Shoot-’Em-Up

This entry is part 2 of 2 in the series An Introduction to FlashPunk

In the last tutorial, we introduced FlashPunk and its capabilities. Now it’s time to build a game with it! We’ll build a top-down, mouse-controlled shoot-’em-up, with a title screen and a game over screen. Read on to learn more…


Final Result Preview

Let’s take a look at the final result we will be working towards:


Step 1: The Player Ship

As always, first we need a clean project. Grab the latest FlashPunk build from the official site. Create a new AS3 project in FlashDevelop and put FlashPunk’s source in the source folder of the project. The game will have the following dimensions: 400×500 px.

We will begin our game by adding a ship – the player ship – on the screen. For that, we will need a new world, called GameWorld, and an image:

Player Ship Image

The player ship will be an entity. Create one, embed the image in it and put it on the screen. If you’re feeling lost, below is the code for the player ship (if you’re really feeling lost, I recommend reading the first tutorial again). I think that creating the world won’t be a problem for you.

package
{
	import net.flashpunk.Entity;
	import net.flashpunk.graphics.Image;

	public class PlayerShip extends Entity
	{
		[Embed(source = '../img/PlayerShipImage.png')]
		private const IMAGE:Class;

		public function PlayerShip()
		{
			graphic = new Image(IMAGE);

			graphic.x = -27.5;
			graphic.y = -30;

			x = 200;
			y = 400;
		}

	}

}

Add the player ship into the world and make it the current world when FlashPunk’s Engine starts. You’ll get the following:

The player ship in game

Step 2: Movement

With the player ship on the screen, we need to make it move. Just like every shoot-’em-up game, the player ship will be able to move across all the screen. There’s one thing left to decide before coding the movement: we will be using frame-based movement (the reason for this is in the next step). That means changing (if needed) the super() call to FlashPunk’s Engine and NOT using the FP.elapsed property.

The code for the player ship movement is below. The movement is mouse-based:

		private var _currentDistanceX:Number;
		private var _currentDistanceY:Number;

		private const SPEED:int = 3;

		override public function update():void
		{
			calculateDistances();

			if ((_currentDistanceX * _currentDistanceX) + (_currentDistanceY * _currentDistanceY) < SPEED * SPEED)
			{
				x = Input.mouseX;
				y = Input.mouseY;
			}
			else
			{
				x += Math.cos(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED;
				y += Math.sin(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED;
			}
		}

		private function calculateDistances():void
		{
			_currentDistanceX = Input.mouseX - x;
			_currentDistanceY = Input.mouseY - y;
		}

You will need to import net.flashpunk.utils.Input in the player ship class. Also, don’t forget to enable FlashPunk’s console. After compiling the project, you will see the player ship following the mouse:


Step 3: Enemies

Time to add something fun: enemies. Enemies need to spawn randomly (based on waves) and need to have a controlled movement. They also have a different image:

Image of the enemy

The code for creating the enemy class and adding the image to it shouldn’t be a problem. What we need to focus on is how to make enemies follow a path. The idea is this: we create a wave of enemies and pass them a path to follow, and when to spawn. After that, they will be responsible for showing themselves on the screen and moving along the path.

What is a good representation for a path? A Vector of points seems good enough. Also, we need to define a distance between each point. That way, we can be sure of one important thing: if we define the distance between two points of the path to be the distance that the enemy moves during 1 frame, we won’t need the enemy to process too much information, and we will be able to define after how many frames the enemy will leave the screen by just counting the number of points in the vector. That way, it’s very easy to define when a wave will end.

See it in action: the code below only makes enemies follow a path already given to them, and on the time passed to them (which will be counted in frames). When the enemy is created, a counter that starts on a given number begins decreasing after each elapsed frame, and when it reaches zero, the enemy will put itself on screen and begin following its path.

package
{
	import flash.geom.Point;
	import net.flashpunk.Entity;
	import net.flashpunk.FP;
	import net.flashpunk.graphics.Image;
	import net.flashpunk.World;

	public class Enemy extends Entity
	{
		[Embed(source = '../img/EnemyImage.png')]
		private const IMAGE:Class;

		private var _timeToAct:uint;
		private var _pathToFollow:Vector.<Point>;

		private var _currentPoint:uint;

		private var _myWorld:World;
		private var _added:Boolean;

		public function Enemy(timeToAct:uint, pathToFollow:Vector.<Point>, worldToBeAdded:World)
		{
			graphic = new Image(IMAGE);

			graphic.x = -15;
			graphic.y = -8;

			_timeToAct = timeToAct;

			_pathToFollow = pathToFollow;

			_currentPoint = 0;

			_myWorld = worldToBeAdded;
			_added = false;
		}

		override public function update():void
		{
			if (_timeToAct > 0)
			{
				_timeToAct--;
			}
			else
			{
				if (!_added)
				{
					_myWorld.add(this);

					_added = true;
				}

				x = _pathToFollow[_currentPoint].x;
				y = _pathToFollow[_currentPoint].y;

				_currentPoint++;

				if (_currentPoint == _pathToFollow.length)
				{
					_myWorld.remove(this);

					_added = false;

					destroy();
				}
			}
		}

		public function destroy():void
		{
			graphic = null;
		}

	}

}

From the code above, you can see that we always move a fixed distance from point to point by just positioning the enemy on its current point. It is because of this that we are able to determine when an enemy wave will end. You can also see that the enemy takes care of everything: adding itself in the world, moving through the world and removing itself from the world. With that, it’s very simple to create enemies in the game.


Step 4: Adding Enemies to the Screen

Our base Enemy class is done. Now it’s time to modify GameWorld a bit to add enemies. The first task is to generate paths for the enemies. For the purposes of this tutorial, we will only create a straight line, but feel free to try creating any kind of wave path. This is the function that creates a straight line:

		private function generateEnemyPath(distanceBetweenPoints:Number):Vector.<Point>
		{
			var i:Number;

			var vec:Vector.<Point> = new Vector.<Point>();

			var xPos:Number = Math.random() * 360 + 20;

			for (i = -20; i < 520; i += distanceBetweenPoints)
			{
				vec.push(new Point(xPos, i));
			}

			return vec;
		}

With that, we can already give to an enemy a path to follow. The next step is to actually create the enemy:

		private var _enemy:Enemy;

		public function GameWorld()
		{
			_playerShip = new PlayerShip();

			add(_playerShip);

			_enemy = new Enemy(0, generateEnemyPath(1), this);
		}

		override public function update():void
		{
			super.update();

			if (_enemy)
				_enemy.update();
		}

Compile and run the game. You’ll probably get the following error:

[Fault] exception, information=RangeError: Error #1125: The index 540 is out of range 540.

That happens because even after the enemy deletes itself from the world, we are still calling the update() function of it, because our code didn’t detect when the enemy removed itself. Let’s fix that by overriding the current remove() method:

		override public function remove(e:Entity):Entity
		{
			if (e is Enemy)
			{
				_enemy = new Enemy(0, generateEnemyPath(1), this);
			}

			return super.remove(e);
		}

Now compile the project and you’ll see this:

This is what that function does: every time the enemy removes itself from the world, we detect that through our overriden function and then just create another enemy to “replace” the old one. That’s it! We now have enemies moving across the screen!


Step 5: Shooting With the Player Ship

A shoot-’em-up game wouldn’t be fun without cool ways of shooting bullets out of your ship, would it? In this step we’ll see a great way of organizing bullet patterns in order to shoot them. If you tried to take a guess, you are probably correct: we’re going to use Vectors of Points. However, this time the points will have dynamic beginnings and ends, because your ship won’t always be at the same place every time you shoot, but don’t worry, it’s not as hard as it sounds!

The strategy here is to generate a bullet pattern around a fixed x- and y-axis, and then sum the player ship’s position to the points from the pattern, thus relocating the axis to a new position, giving the impression that the bullets are coming out of the player ship. The bullet image we are going to use is this:

Player bullet

Everything gets simpler when you look at the code. We are basically doing something very similar to the enemy path:

package
{
	import flash.geom.Point;
	import net.flashpunk.Entity;
	import net.flashpunk.graphics.Image;

	public class PlayerBullet extends Entity
	{
		[Embed(source = '../img/BulletImage.png')]
		private const IMAGE:Class;

		private var _pathToFollow:Vector.<Point>;

		private var _xPos:Number;
		private var _yPos:Number;

		public function PlayerBullet(pathToFollow:Vector.<Point>, xPos:Number, yPos:Number)
		{
			graphic = new Image(IMAGE);

			graphic.x = graphic.y = -3.5;

			_pathToFollow = pathToFollow;

			_xPos = xPos;
			_yPos = yPos;
		}

		override public function update():void
		{
			x = _xPos + _pathToFollow[0].x;
			y = _yPos + _pathToFollow[0].y;

			_pathToFollow.shift();

			if (_pathToFollow.length == 0)
			{
				world.remove(this);

				destroy();
			}
		}

		public function destroy():void
		{
			_pathToFollow = null;

			graphic = null;
		}

	}

}

Notice that we don’t really create the bullet patterns in here: they’re always passed as parameters, just like the enemies. The only difference is that the bullets are always added right away in the world and we keep the initial position of the bullet.

Let’s try adding a bullet when the player clicks with the mouse. In PlayerShip.as:

		override public function update():void
		{
			calculateDistances();

			if ((_currentDistanceX * _currentDistanceX) + (_currentDistanceY * _currentDistanceY) < SPEED * SPEED)
			{
				x = Input.mouseX;
				y = Input.mouseY;
			}
			else
			{
				x += Math.cos(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED;
				y += Math.sin(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED;
			}

			if (Input.mousePressed)
			{
				world.add(new PlayerBullet(GameWorld(world).generateBulletPath(3), x, y));
			}
		}

Now we need to create the bullet path. We are going to create a straight line, just like for the enemy, but you can do any kind of path! In GameWorld.as, let’s create the generateBulletPath() function:

		public function generateBulletPath(distanceBetweenPoints:Number):Vector.<Point>
		{
			var i:Number;

			var vec:Vector.<Point> = new Vector.<Point>();

			for (i = 0; i > -500; i -= distanceBetweenPoints)
			{
				vec.push(new Point(0, i));
			}

			return vec;
		}

With that, hit the compile and run button and this is what you get:


Step 6: Collision Detection (Using Masks)

We have now the basics of the game running: a player ship that shoots and enemies that go down the screen. Time to add collision detection!

The first step to add the collision detection is to give each entity a type. I’ll leave that to you: give the “Player” type to PlayerShip, “Enemy” to Enemy and “PlayerBullet” to PlayerBullet.

We will be using pixel-perfect collision in this game, so it might be useful to talk about masks. Masks are elements used by FlashPunk for collision detection. They are basically like hitboxes, but they can have a different form (pixel level). We need to set up masks for the player ship, enemies and bullets. The image used by the mask is the same image of the entity. Look at the code for the PlayerShip, Enemy and PlayerBullet, respectively:

		public function PlayerShip()
		{
			graphic = new Image(IMAGE);

			graphic.x = -27.5;
			graphic.y = -30;

			mask = new Pixelmask(IMAGE, -27.5, -30);

			x = 200;
			y = 400;

			type = "Player";
		}
		public function Enemy(timeToAct:uint, pathToFollow:Vector.<Point>, worldToBeAdded:World)
		{
			graphic = new Image(IMAGE);

			graphic.x = -15;
			graphic.y = -8;

			mask = new Pixelmask(IMAGE, -15, -8);

			_timeToAct = timeToAct;

			_pathToFollow = pathToFollow;

			_currentPoint = 0;

			_myWorld = worldToBeAdded;
			_added = false;

			type = "Enemy";
		}
		public function PlayerBullet(pathToFollow:Vector.<Point>, xPos:Number, yPos:Number)
		{
			graphic = new Image(IMAGE);

			graphic.x = graphic.y = -3.5;

			mask = new Pixelmask(IMAGE, -3.5, -3.5);

			_pathToFollow = pathToFollow;

			_xPos = xPos;
			_yPos = yPos;

			type = "PlayerBullet";
		}

As you can see, this is very simple: we create a new Pixelmask, pass the source to use as a mask (just like with the graphic) and then pass both x and y offsets (in case you want to center the mask somewhere). Now, in GameWorld.as:

		private var _bulletList:Vector.<PlayerBullet>;

		override public function update():void
		{
			super.update();

			if (_enemy)
				_enemy.update();

			_bulletList = new Vector.<PlayerBullet>();

			getType("PlayerBullet", _bulletList);

			for each (var bullet:PlayerBullet in _bulletList)
			{
				if (bullet.collideWith(_enemy, bullet.x, bullet.y))
				{
					_enemy.takeDamage();

					remove(bullet);

					bullet.destroy();
				}
			}
		}

Notice that we could have simply used _enemy.collide("PlayerBullet", _enemy.x, _enemy.y) to check for collisions, but the method above is better when we have many bullets on the screen and there is a possibility that two bullets hit the same enemy at the same time. We have called the takeDamage() function of the Enemy class, but at the moment there is none. (Create an empty function for now. In the next step we will make the enemy take damage and explode when necessary.) Compile the project and you’ll get this:


Step 7: Enemy Death

We have made bullets hit our enemies. In this step we will play an explosion animation every time an enemy dies and remove it from the game. The explosion animation sprite sheet is below:

Explosion after enemy death

The approach we will take to do this is by decreasing the enemy’s health in the takeDamage() function, and if the health gets below zero, we will destroy it and put the animation in the screen. The code for decreasing the health is below:

		private var _health:int = 100;

		public function takeDamage():void
		{
			_health -= 50;

			if (_health <= 0)
			{
				addExplosion();

				_myWorld.remove(this);

				_added = false;

				destroy();
			}
		}

The code is very simple. There’s only one thing unknown about it: the addExplosion() function. This function will create an instance of Explosion and add it to the world. The Explosion class will just play and remove itself from the world after that. It’s a simple class:

package
{
	import net.flashpunk.Entity;
	import net.flashpunk.graphics.Spritemap;

	public class Explosion extends Entity
	{
		[Embed(source = '../img/ExplosionAnimation.png')]
		private const ANIMATION:Class;

		public function Explosion(xPos:Number, yPos:Number)
		{
			graphic = new Spritemap(ANIMATION, 50, 46, onAnimationEnd);

			graphic.x = -25;
			graphic.y = -23;

			x = xPos;
			y = yPos;

			Spritemap(graphic).add("Explode", [0, 1, 2, 3, 4], 25/60, false);

			Spritemap(graphic).play("Explode");
		}

		private function onAnimationEnd():void
		{
			world.remove(this);

			destroy();
		}

		public function destroy():void
		{
			graphic = null;
		}

	}

}

The trick here is using the callback parameter of the Spritemap class: when the animation ends, that function will be called, and then it removes itself from the world.

Now, back to Enemy.as to finish that function!

		private function addExplosion():void
		{
			world.add(new Explosion(x, y));
		}

Easy, isn’t it? Compile the game and destroy some enemies!


Step 8: Score

The next logical step after making enemies die is to add a score in the game! We will do that using FlashPunk’s Text class. Start by creating a GameScore class, which will hold the score of the game. Since Text is a Graphic, we will make GameScore an Entity and add its text as a graphic. Look at the code:

package
{
	import net.flashpunk.Entity;
	import net.flashpunk.graphics.Text;

	public class GameScore extends Entity
	{
		private var _score:int;

		public function GameScore()
		{
			graphic = new Text("Score: 0");

			_score = 0;
		}

		public function addScore(points:int):void
		{
			_score += points;

			Text(graphic).text = "Score: " + _score.toString();
		}

		public function destroy():void
		{
			graphic = null;
		}

	}

}

As you can see, we will call the addScore() function to add points to the game’s score. First, we need to add it to the world. In GameWorld.as:

		private var _score:GameScore;

		public function GameWorld()
		{
			_playerShip = new PlayerShip();

			add(_playerShip);

			_enemy = new Enemy(0, generateEnemyPath(1), this);

			_score = new GameScore();

			_score.x = 300;
			_score.y = 470;

			add(_score);
		}

		public function get score():GameScore
		{
			return _score;
		}

		public function get score():int
		{
			return _score;
		}

If we hit compile, we get just a static score on the bottom of the screen:

The game score

We need to add something to the score every time an enemy is killed. In Enemy.as:

		public function takeDamage():void
		{
			_health -= 50;

			if (_health <= 0)
			{
				addExplosion();

				GameWorld(world).score.addScore(1);

				_myWorld.remove(this);

				_added = false;

				destroy();
			}
		}

Hit compile now and the score will always increase when an enemy is killed!


Step 9: Upgrades

Time to add upgrades! We will not have a nice screen to choose upgrades. Instead, we will create upgrades based on the score: each time the score goes up by 5 (up to 45), the player’s speed will increase a bit. When the score reaches 25, the player will be able to shoot two shots on every click. We will make the score 50 be the end of the game.

Let’s begin by coding the player speed upgrade. For that, we will need to add a multiplier to the speed. In PlayerShip.as:

		public var speedMultiplier:Number = 1;

		override public function update():void
		{
			calculateDistances();

			if ((_currentDistanceX * _currentDistanceX) + (_currentDistanceY * _currentDistanceY) < SPEED * SPEED * speedMultiplier * speedMultiplier)
			{
				x = Input.mouseX;
				y = Input.mouseY;
			}
			else
			{
				x += Math.cos(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED * speedMultiplier;
				y += Math.sin(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED * speedMultiplier;
			}

			if (Input.mousePressed)
			{
				world.add(new PlayerBullet(GameWorld(world).generateBulletPath(3), x, y));
			}
		}

Now it’s all about changing the multiplier in GameWorld.as:

		private var _speedUpgradeNumber:int = 0;

		override public function update():void
		{
			super.update();

			if (_enemy)
				_enemy.update();

			_bulletList = new Vector.<PlayerBullet>();

			getType("PlayerBullet", _bulletList);

			for each (var bullet:PlayerBullet in _bulletList)
			{
				if (bullet.collideWith(_enemy, bullet.x, bullet.y))
				{
					_enemy.takeDamage();

					remove(bullet);

					bullet.destroy();
				}
			}

			if ((_score.score % 5) == 0 && _score.score > (_speedUpgradeNumber * 5))
			{
				_playerShip.speedMultiplier += 0.1;

				_speedUpgradeNumber++;
			}
		}

Done! Now, every 5 enemy deaths the player will receive a 10% increase in moving speed!

For the double bullet upgrade, we will make a boolean in the PlayerShip class indicating whether or not the ship has the upgrade. Then we will check that when shooting. Here is it:

		public var hasDoubleShoot:Boolean = false;

		override public function update():void
		{
			calculateDistances();

			if ((_currentDistanceX * _currentDistanceX) + (_currentDistanceY * _currentDistanceY) < SPEED * SPEED * speedMultiplier * speedMultiplier)
			{
				x = Input.mouseX;
				y = Input.mouseY;
			}
			else
			{
				x += Math.cos(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED * speedMultiplier;
				y += Math.sin(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED * speedMultiplier;
			}

			if (Input.mousePressed)
			{
				if (hasDoubleShoot)
				{
					world.add(new PlayerBullet(GameWorld(world).generateBulletPath(3), x - 5, y));
					world.add(new PlayerBullet(GameWorld(world).generateBulletPath(3), x + 5, y));
				}
				else
				{
					world.add(new PlayerBullet(GameWorld(world).generateBulletPath(3), x, y));
				}
			}
		}

Now, let’s do the same as we did for the speed in GameWorld.as:

		override public function update():void
		{
			super.update();

			if (_enemy)
				_enemy.update();

			_bulletList = new Vector.<PlayerBullet>();

			getType("PlayerBullet", _bulletList);

			for each (var bullet:PlayerBullet in _bulletList)
			{
				if (bullet.collideWith(_enemy, bullet.x, bullet.y))
				{
					_enemy.takeDamage();

					remove(bullet);

					bullet.destroy();
				}
			}

			if ((_score.score % 5) == 0 && _score.score > (_speedUpgradeNumber * 5))
			{
				_playerShip.speedMultiplier += 0.1;

				_speedUpgradeNumber++;
			}

			if (_score.score >= 25)
			{
				_playerShip.hasDoubleShoot = true;
			}
		}

And that’s it! Hit compile and your game will have upgrades!


Step 10: Increasing the Difficulty

If you played the game until getting the double shot upgrade in the last step, you may have noticed that the game is too easy right now. What do you think of increasing its difficulty based on the player’s score?

That’s the idea: the game will now be able to put on the screen more than one enemy. And for each kill the player gets, we decrease the timer of spawning a new enemy. Interesting, isn’t it? But that’s not the only thing. What about increasing the enemies’ healths and decreasing the damage they take from the player? Now we’re getting somewhere!

Let’s jump to the coding: first we will make the health increase and damage decrease every kill. They will be just like the upgrades on the player ships, but this time we will need to change our approach as to where to keep the multipliers. In Enemy.as:

		private var _health:int;

		public static var healthMultiplier:Number = 1;
		public static var damageMultiplier:Number = 1;

		public function Enemy(timeToAct:uint, pathToFollow:Vector.<Point>, worldToBeAdded:World)
		{
			graphic = new Image(IMAGE);

			graphic.x = -15;
			graphic.y = -8;

			mask = new Pixelmask(IMAGE, -15, -8);

			_timeToAct = timeToAct;

			_pathToFollow = pathToFollow;

			_currentPoint = 0;

			_myWorld = worldToBeAdded;
			_added = false;

			type = "Enemy";

			health = 100 * Enemy.healthMultiplier;
		}

		public function takeDamage():void
		{
			_health -= 50 * Enemy.damageMultiplier;

			if (_health <= 0)
			{
				addExplosion();

				GameWorld(world).score.addScore(1);

				_myWorld.remove(this);

				_added = false;

				destroy();
			}
		}

And now, in GameWorld.as:

		private var _enemyUpgradeNumber:int = 0;

		override public function update():void
		{
			super.update();

			if (_enemy)
				_enemy.update();

			_bulletList = new Vector.<PlayerBullet>();

			getType("PlayerBullet", _bulletList);

			for each (var bullet:PlayerBullet in _bulletList)
			{
				if (bullet.collideWith(_enemy, bullet.x, bullet.y))
				{
					_enemy.takeDamage();

					remove(bullet);

					bullet.destroy();
				}
			}

			if ((_score.score % 5) == 0 && _score.score > (_speedUpgradeNumber * 5))
			{
				_playerShip.speedMultiplier += 0.1;

				_speedUpgradeNumber++;
			}

			if (_score.score >= 25)
			{
				_playerShip.hasDoubleShoot = true;
			}

			if (_score.score > _enemyUpgradeNumber)
			{
				Enemy.damageMultiplier -= 0.015;
				Enemy.healthMultiplier += 0.02;

				_enemyUpgradeNumber++;
			}
		}

And now our enemies are getting stronger after each kill! Take that, evil player!

Now we need to modify GameWorld in order to spawn enemies based on time. It’s a simple thing: we will just need a timer and a spawn interval. Here’s all the modified code:

		private var _enemyList:Vector.<Enemy>;

		private var _enemySpawnInterval:int = 5000;
		private var _enemySpawnTimer:int;

		public function GameWorld()
		{
			_playerShip = new PlayerShip();

			add(_playerShip);

			_enemyList = new Vector.<Enemy>();

			_score = new GameScore();

			_score.x = 300;
			_score.y = 470;

			add(_score);

			_enemySpawnTimer = 0;
		}

		override public function update():void
		{
			super.update();

			_enemySpawnTimer--;

			if (_enemySpawnTimer <= 0)
			{
				_enemySpawnTimer = _enemySpawnInterval;

				_enemyList.push(new Enemy(uint(Math.random() * 30), generateEnemyPath(2), this));

				add(_enemyList[_enemyList.length - 1]);
			}

			_bulletList = new Vector.<PlayerBullet>();

			getType("PlayerBullet", _bulletList);

			for each (var bullet:PlayerBullet in _bulletList)
			{
				for each (var enemy:Enemy in _enemyList)
				{
					if (bullet.collideWith(enemy, bullet.x, bullet.y))
					{
						enemy.takeDamage();

						remove(bullet);

						bullet.destroy();
					}
				}
			}

			if ((_score.score % 5) == 0 && _score.score > (_speedUpgradeNumber * 5))
			{
				_playerShip.speedMultiplier += 0.1;

				_speedUpgradeNumber++;
			}

			if (_score.score >= 25)
			{
				_playerShip.hasDoubleShoot = true;
			}

			if (_score.score > _enemyUpgradeNumber)
			{
				Enemy.damageMultiplier -= 0.015;
				Enemy.healthMultiplier += 0.02;

				_enemyUpgradeNumber++;
			}
		}

		override public function remove(e:Entity):Entity
		{
			if (e is Enemy)
			{
				_enemyList.splice(_enemyList.indexOf(e), 1);
			}

			return super.remove(e);
		}

Notice that I gave the enemies a few random frames of “wait time” before appearing. That will make their appearance unpredictable. Now all that’s left is to decrease the spawn interval after every kill:

		override public function update():void
		{
			super.update();

			_enemySpawnTimer--;

			if (_enemySpawnTimer <= 0)
			{
				_enemySpawnTimer = _enemySpawnInterval;

				_enemyList.push(new Enemy(uint(Math.random() * 30), generateEnemyPath(2), this));

				add(_enemyList[_enemyList.length - 1]);
			}

			_bulletList = new Vector.<PlayerBullet>();

			getType("PlayerBullet", _bulletList);

			for each (var bullet:PlayerBullet in _bulletList)
			{
				for each (var enemy:Enemy in _enemyList)
				{
					if (bullet.collideWith(enemy, bullet.x, bullet.y))
					{
						enemy.takeDamage();

						remove(bullet);

						bullet.destroy();
					}
				}
			}

			if ((_score.score % 5) == 0 && _score.score > (_speedUpgradeNumber * 5))
			{
				_playerShip.speedMultiplier += 0.1;

				_speedUpgradeNumber++;
			}

			if (_score.score >= 25)
			{
				_playerShip.hasDoubleShoot = true;
			}

			if (_score.score > _enemyUpgradeNumber)
			{
				Enemy.damageMultiplier -= 0.015;
				Enemy.healthMultiplier += 0.02;

				_enemySpawnInterval -= 3;

				_enemyUpgradeNumber++;
			}
		}

With that, we basically have everything done in the game world!


Step 11: The Main Menu World

I think we have everything finished in the game world. The game’s surprisingly difficult with the tougher enemies! What do you think of making a nice main menu now? I created a nice background for it:

The main menu screen

Create the MainMenuWorld class, which extends from net.flashpunk.World, and add the background in it. I’ll leave the code for you. We will need a play button, which I also created:

The play button

In order to create the play button, we are going to use the Button class created in the first part of this tutorial series. Here’s the code for the button in MainMenuWorld.as:

		[Embed(source = '../img/PlayGameButton.png')]
		private const PLAYBUTTON:Class;

		private var _playButton:Button;

		public function MainMenuWorld()
		{
			addGraphic(new Image(TITLE));

			_playButton = new Button(playTheGame, null, 48, 395);

			_playButton.setSpritemap(PLAYBUTTON, 312, 22);

			add(_playButton);
		}

		private function playTheGame():void
		{
			FP.world = new GameWorld();

			destroy();
		}

		public function destroy():void
		{
			removeAll();

			_playButton = null;
		}

Don’t forget to change the Main class as well!

		override public function init():void
		{
			FP.world = new MainMenuWorld();

			FP.console.enable();
		}

Hit compile and… Yes! Our shiny main menu world works! Now to the game over world!


Step 12: The Game Over World

The game over world is going to be very simple. I have created two images: one for when the player dies and one for when the player wins the game. There will be a Quit button that will return the player to the main menu. It’s basically the same thing as the main menu world. Here are the two images and the button:

Game over image when player loses
Game over image when player wins
The quit button

I will leave the coding to you. The only thing that will change in this class is that it will need an argument passed to the constructor, telling whether or not the player destroyed the enemies. Here’s the code for the constructor:

		public function GameOverWorld(hasLost:Boolean)
		{
			if (hasLost)
			{
				addGraphic(new Image(BACKGROUNDLOST));
			}
			else
			{
				addGraphic(new Image(BACKGROUNDWON));
			}

			_quitButton = new Button(quitToMain, null, 166, 395);

			_quitButton.setSpritemap(QUITBUTTON, 69, 19);

			add(_quitButton);
		}

Step 13: The Boss – Movement

Finally, the moment everyone was waiting for. Every shoot-’em-up needs a boss, and this is ours!

The boss! Kill him!

What we need now is code it. First, the movements. And then, the bullets. This step is for the movements.

As you may have guessed, our boss won’t go directly down the screen. Instead, it will move randomly around the top of the screen, to give the player a bit of difficulty defeating it. What we will need to do is a movement very similar to the player’s. The only difference is that it will follow a point chosen randomly in the top of the screen, and not the mouse. Here’s the full code for the Boss class!

package
{
	import flash.geom.Point;
	import net.flashpunk.Entity;
	import net.flashpunk.graphics.Image;
	import net.flashpunk.masks.Pixelmask;

	public class Boss extends Entity
	{
		[Embed(source = '../img/BossImage.png')]
		private const IMAGE:Class;

		private var _currentDistanceX:Number;
		private var _currentDistanceY:Number;

		private var _randomPoint:Point;

		private const SPEED:int = 3;

		public var speedMultiplier:Number = 1;

		public function Boss()
		{
			graphic = new Image(IMAGE);

			graphic.x = -38;
			graphic.y = -35;

			mask = new Pixelmask(IMAGE, -38, -35);

			type = "BossEnemy";

			getRandomPoint();
		}

		private function getRandomPoint():void
		{
			_randomPoint = new Point();

			_randomPoint.x = Math.random() * 400;
			_randomPoint.y = 38 + (Math.random() * 100);
		}

		override public function update():void
		{
			calculateDistances();

			if ((_currentDistanceX * _currentDistanceX) + (_currentDistanceY * _currentDistanceY) < SPEED * SPEED * speedMultiplier * speedMultiplier)
			{
				x = _randomPoint.x;
				y = _randomPoint.y;

				getRandomPoint();
			}
			else
			{
				x += Math.cos(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED * speedMultiplier;
				y += Math.sin(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED * speedMultiplier;
			}
		}

		private function calculateDistances():void
		{
			_currentDistanceX = _randomPoint.x - x;
			_currentDistanceY = _randomPoint.y - y;
		}

	}

}

We are basically using the same movement code from PlayerShip. Notice that we have kept the speed multipliers, because there will be a really fun thing in the end!

We need now to come up with a way to test this movement. Running the game and getting 50 kills is too much time to wait until we can see the boss (and end up realizing there’s a bug and we will need to do it all again and again!). Let’s just add the boss on the screen when the game begins (yes, with the other enemies going down the screen!) and check the movement! In GameWorld.as:

		private var _boss:Boss;

		public function GameWorld()
		{
			_playerShip = new PlayerShip();

			add(_playerShip);

			_enemyList = new Vector.<Enemy>();

			_score = new GameScore();

			_score.x = 300;
			_score.y = 470;

			add(_score);

			_enemySpawnTimer = 0;

			_boss = new Boss();

			add(_boss);
		}

Hit compile and test the game! Our boss is moving nicely, isn’t it? It’s going to be hard to kill it!


Step 14: The Boss – Shooting

Time for the really evil boss bullets! Here is their image:

Boss bullet

But there is something really bothering me: they will have exactly the same behavior as the player bullet, but instead they will just follow the same enemy downward path, and will have a different FlashPunk type, but I don’t want to copy and paste the same code in their class. What do you think about using some Object-Oriented Design and make an inheritance? Take all the code (yes, all the code) of PlayerBullet and copy it to a new class called Bullet. Remove the code related to the bullet’s image, and this is what you get:

package
{
	import flash.geom.Point;
	import net.flashpunk.Entity;

	public class Bullet extends Entity
	{
		private var _pathToFollow:Vector.<Point>;

		private var _xPos:Number;
		private var _yPos:Number;

		public function Bullet(pathToFollow:Vector.<Point>, xPos:Number, yPos:Number)
		{
			_pathToFollow = pathToFollow;

			_xPos = xPos;
			_yPos = yPos;
		}

		override public function update():void
		{
			x = _xPos + _pathToFollow[0].x;
			y = _yPos + _pathToFollow[0].y;

			_pathToFollow.shift();

			if (_pathToFollow.length == 0)
			{
				world.remove(this);

				destroy();
			}
		}

		public function destroy():void
		{
			_pathToFollow = null;

			graphic = null;
		}

	}

}

That’s the basic behavior of the bullet. Now, what do we do with the PlayerBullet class? Put only the things related to the bullet image in there, and remove the rest. And also make it inherit from Bullet:

package
{
	import flash.geom.Point;
	import net.flashpunk.graphics.Image;
	import net.flashpunk.masks.Pixelmask;

	public class PlayerBullet extends Bullet
	{
		[Embed(source = '../img/BulletImage.png')]
		private const IMAGE:Class;

		public function PlayerBullet(pathToFollow:Vector.<Point>, xPos:Number, yPos:Number)
		{
			super(pathToFollow, xPos, yPos);

			graphic = new Image(IMAGE);

			graphic.x = graphic.y = -3.5;

			mask = new Pixelmask(IMAGE, -3.5, -3.5);

			type = "PlayerBullet";
		}

	}

}

Do you mind making the same thing for the BossBullet class too? The only difference is that it will have a type of “BossBullet”. I’ll leave the code to you. Check the tutorial source if you need help!

We could have done the same for the boss and the enemies, but we would need to change more things, because the movement of the enemies isn’t the same as the movement of the boss. It’s better to leave it that way. And now, for the boss shooting. We will use the same timer strategy that we used for the enemy spawning. Here’s the code:

		private var _shootingInterval:int = 75;
		private var _shootingTimer:int = 0;

		override public function update():void
		{
			calculateDistances();

			if ((_currentDistanceX * _currentDistanceX) + (_currentDistanceY * _currentDistanceY) < SPEED * SPEED * speedMultiplier * speedMultiplier)
			{
				x = _randomPoint.x;
				y = _randomPoint.y;

				getRandomPoint();
			}
			else
			{
				x += Math.cos(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED * speedMultiplier;
				y += Math.sin(Math.atan2(_currentDistanceY, _currentDistanceX)) * SPEED * speedMultiplier;
			}

			_shootingTimer--;

			if (_shootingTimer <= 0)
			{
				_shootingTimer = _shootingInterval;

				world.add(new BossBullet(GameWorld(world).generateBossBulletPath(1.5), x - 10, y));
				world.add(new BossBullet(GameWorld(world).generateBossBulletPath(1.5), x + 10, y));
				world.add(new BossBullet(GameWorld(world).generateBossBulletPath(1.5), x, y + 5));
			}
		}

Compile and run the code and now you have a boss shooting bullets and flying around! Time to make the last battle happen in the next step :)


Step 15: Final Touches

You may have noticed that the boss’s bullets don’t hit the player ship, and the player’s bullets don’t hit the boss. That’s because we haven’t coded them to hit their enemies. That’s what we are going to do now. We will also make the boss only appear when the score reaches 50, and check for when the player loses the game.

The first task: to make the boss only appear when the score reaches 50. We will do that by checking for the score in GameWorld.as, just like we did with the enemy and player upgrades.

		private var _bossAppeared:Boolean = false;

		public function GameWorld()
		{
			_playerShip = new PlayerShip();

			add(_playerShip);

			_enemyList = new Vector.<Enemy>();

			_score = new GameScore();

			_score.x = 300;
			_score.y = 470;

			add(_score);

			_enemySpawnTimer = 0;

			_boss = new Boss();

			// Not adding the boss in the game this time
		}

		override public function update():void
		{
			super.update();

			_enemySpawnTimer--;

			if (_enemySpawnTimer <= 0 && !_bossAppeared)
			{
				_enemySpawnTimer = _enemySpawnInterval;

				_enemyList.push(new Enemy(uint(Math.random() * 30), generateEnemyPath(2), this));

				add(_enemyList[_enemyList.length - 1]);
			}

			_bulletList = new Vector.<PlayerBullet>();

			getType("PlayerBullet", _bulletList);

			for each (var bullet:PlayerBullet in _bulletList)
			{
				for each (var enemy:Enemy in _enemyList)
				{
					if (bullet.collideWith(enemy, bullet.x, bullet.y))
					{
						enemy.takeDamage();

						remove(bullet);

						bullet.destroy();
					}
				}
			}

			if ((_score.score % 5) == 0 && _score.score > (_speedUpgradeNumber * 5))
			{
				_playerShip.speedMultiplier += 0.1;

				_speedUpgradeNumber++;
			}

			if (_score.score >= 25)
			{
				_playerShip.hasDoubleShoot = true;
			}

			if (_score.score > _enemyUpgradeNumber)
			{
				Enemy.damageMultiplier -= 0.015;
				Enemy.healthMultiplier += 0.02;

				_enemySpawnInterval -= 3;

				_enemyUpgradeNumber++;
			}

			if (_score.score == 50 && !_bossAppeared)
			{
				add(_boss);

				_bossAppeared = true;
			}
		}

First task done. Notice that we also changed the block that spawned new enemies. That way, when the boss appears, no more enemies will spawn.

Second task: make boss bullets hit the player and player bullets hit the boss. This is also done in GameWorld.as. Take a look at the code:

		private var _bulletList:Vector.<Bullet>;

		override public function update():void
		{
			super.update();

			_enemySpawnTimer--;

			if (_enemySpawnTimer <= 0 && !_bossAppeared)
			{
				_enemySpawnTimer = _enemySpawnInterval;

				_enemyList.push(new Enemy(uint(Math.random() * 30), generateEnemyPath(2), this));

				add(_enemyList[_enemyList.length - 1]);
			}

			_bulletList = new Vector.<Bullet>();

			getType("PlayerBullet", _bulletList);

			for each (var bullet:PlayerBullet in _bulletList)
			{
				for each (var enemy:Enemy in _enemyList)
				{
					if (bullet.collideWith(enemy, bullet.x, bullet.y))
					{
						enemy.takeDamage();

						remove(bullet);

						bullet.destroy();
					}
				}

				if (_bossAppeared)
				{
					if (bullet.collideWith(_boss, bullet.x, bullet.y))
					{
						_boss.takeDamage();

						if (!_boss)
						{
							endTheGame();

							return;
						}

						remove(bullet);

						bullet.destroy();
					}
				}
			}

			_bulletList = new Vector.<Bullet>();

			getType("BossBullet", _bulletList);

			for each (var bossBullet:BossBullet in _bulletList)
			{
				if (bossBullet.collideWith(_playerShip, bossBullet.x, bossBullet.y))
				{
					endTheGame();

					return;
				}
			} 

			if ((_score.score % 5) == 0 && _score.score > (_speedUpgradeNumber * 5))
			{
				_playerShip.speedMultiplier += 0.1;

				_speedUpgradeNumber++;
			}

			if (_score.score >= 25)
			{
				_playerShip.hasDoubleShoot = true;
			}

			if (_score.score > _enemyUpgradeNumber)
			{
				Enemy.damageMultiplier -= 0.015;
				Enemy.healthMultiplier += 0.02;

				_enemySpawnInterval -= 3;

				_enemyUpgradeNumber++;
			}

			if (_score.score == 50 && !_bossAppeared)
			{
				add(_boss);

				_bossAppeared = true;
			}
		}

In the first part of the code, we check for a collision between the player’s bullets and the boss. If there is a collision, we call the takeDamage() function from the boss, which is below. After checking the player’s bullets, we check for the boss’s bullets, and if we find a collision between any of them and the player, we end the game. This function is also below.

Now, for the takeDamage() function of the boss. We want the boss to have a lot of health, and we will make it fly faster after each hit. In Boss.as:

		private var _health:int = 1000;

		public function takeDamage():void
		{
			_health -= 50;

			speedMultiplier += 0.05;

			if (_health <= 0)
			{
				world.remove(this);

				destroy();
			}
		}

		public function destroy():void
		{
			graphic = null;

			mask = null;

			_randomPoint = null;
		}

Our boss battle is complete! It will die after 20 hits from the player, getting faster after each hit.

Now, the only thing remaining is to end the game and show the screen when the boss is killed, and also make the player lose before the boss battle if an enemy goes down the screen. For the end of the game, we need to check when the boss is killed and create the endTheGame() function in GameWorld.as. This function will basically remove every bullet from the screen, remove the player and boss and then add the game over screen. In GameWorld.as:

		override public function remove(e:Entity):Entity
		{
			if (e is Enemy)
			{
				_enemyList.splice(_enemyList.indexOf(e), 1);
			}

			if (e is Boss)
			{
				_boss = null;
			}

			return super.remove(e);
		}

		public function endTheGame():void
		{
			removeAll();

			while (_bulletList.length > 0)
			{
				_bulletList[0].destroy();

				_bulletList.shift();
			}

			while (_enemyList.length > 0)
			{
				_enemyList[0].destroy();

				_enemyList.shift();
			}

			_bulletList = null;
			_enemyList = null;
			_playerShip = null;
			_score = null;

			if (_boss)
			{
				FP.world = new GameOverWorld(true);
			}
			else
			{
				FP.world = new GameOverWorld(false);
			}

			_boss = null;
		}

And that’s it! The endTheGame() function is basically the same as a destroy() function. It just cleans every reference in the game world.

The last part: making the player lose the game if an enemy has reached the end of the screen. For this one we’re going to remember that an enemy only reaches the bottom of the screen if it still has health. So, in the remove() function of GameWorld.as:

		private var _gameEnded:Boolean = false;

		override public function remove(e:Entity):Entity
		{
			if (e is Enemy)
			{
				if (Enemy(e).health > 0)
				{
					endTheGame();

					return e;
				}

				_enemyList.splice(_enemyList.indexOf(e), 1);
			}

			if (e is Boss)
			{
				_boss = null;
			}

			return super.remove(e);
		}

		override public function update():void
		{
			super.update();

			if (_gameEnded)
			{
				return;
			}

			_enemySpawnTimer--;

			if (_enemySpawnTimer <= 0 && !_bossAppeared)
			{
				_enemySpawnTimer = _enemySpawnInterval;

				_enemyList.push(new Enemy(uint(Math.random() * 30), generateEnemyPath(2), this));

				add(_enemyList[_enemyList.length - 1]);
			}

			_bulletList = new Vector.<Bullet>();

			getType("PlayerBullet", _bulletList);

			for each (var bullet:PlayerBullet in _bulletList)
			{
				for each (var enemy:Enemy in _enemyList)
				{
					if (bullet.collideWith(enemy, bullet.x, bullet.y))
					{
						enemy.takeDamage();

						remove(bullet);

						bullet.destroy();
					}
				}

				if (_bossAppeared)
				{
					if (bullet.collideWith(_boss, bullet.x, bullet.y))
					{
						_boss.takeDamage();

						if (!_boss)
						{
							endTheGame();

							return;
						}

						remove(bullet);

						bullet.destroy();
					}
				}
			}

			_bulletList = new Vector.<Bullet>();

			getType("BossBullet", _bulletList);

			for each (var bossBullet:BossBullet in _bulletList)
			{
				if (bossBullet.collideWith(_playerShip, bossBullet.x, bossBullet.y))
				{
					endTheGame();

					return;
				}
			}

			if ((_score.score % 5) == 0 && _score.score > (_speedUpgradeNumber * 5))
			{
				_playerShip.speedMultiplier += 0.1;

				_speedUpgradeNumber++;
			}

			if (_score.score >= 25)
			{
				_playerShip.hasDoubleShoot = true;
			}

			if (_score.score > _enemyUpgradeNumber)
			{
				Enemy.damageMultiplier -= 0.015;
				Enemy.healthMultiplier += 0.02;

				_enemySpawnInterval -= 3;

				_enemyUpgradeNumber++;
			}

			if (_score.score == 50 && !_bossAppeared)
			{
				add(_boss);

				_bossAppeared = true;
			}
		}

		public function endTheGame():void
		{
			removeAll();

			while (_bulletList.length > 0)
			{
				_bulletList[0].destroy();

				_bulletList.shift();
			}

			while (_enemyList.length > 0)
			{
				_enemyList[0].destroy();

				_enemyList.shift();
			}

			_bulletList = null;
			_enemyList = null;
			_playerShip = null;
			_score = null;

			if (_boss)
			{
				FP.world = new GameOverWorld(true);
			}
			else
			{
				FP.world = new GameOverWorld(false);
			}

			_boss = null;

			_gameEnded = true;
		}

This code will only end the game when an enemy reaches the end (is destroyed) with a health above 0. If the enemy dies, this function is also called, but then the enemy health will be below (or equal to) 0, skipping the code to end the game. We have also created the _gameEnded boolean because when an enemy reaches the end of the screen and gets removed, the world is still updating its entities. Only when it finished updating them (after the super.update() call in the class) is that we can end the game.

After all these lines and lines of code changed, remove the FlashPunk console, hit compile and test the game. It’s finally done, your very first game entirely done in FlashPunk! Congratulations!


Step 16: Conclusion

Congratulations, you have created your first FlashPunk game! You have used pretty much all of FlashPunk’s features for a very basic game, which means you are ready to create more FlashPunk games and spread the word! What do you think of improving this game? It could have different enemies, enemies that also shoot bullets, levels, more bosses, more upgrades and a lot of other things! Are you up to the challenge?



View full post on Activetuts+

Feb 22, 2011 Posted on Feb 22, 2011 in Hints and Tips | 0 comments

Creating Generative Art with HYPE

During this tutorial we’re going to tie math and design together. We’ll explore Branden Hall and Joshua Davis’ HYPE framework and create generative art from code.


Final Result Preview

Let’s take a look at the final result we will be working towards:


Step 1: Project Structure

Before diving head-long into the code let’s take a brief moment to familiarize ourselves with the project files.

Inside the source .zip file you will find a folder for each significant step, so that you can see exactly what changes have been made. Also, you will find a folder called Base, make a copy of this folder as this is will serve as our starting point.

Inside the Base folder we have a bin folder where our SWF will be published. A src folder which contains our ActionScript 3 and Flash Files and lastly a lib folder where we will store the HYPE framework’s SWC files.

Overview of folder structure

Step 2: Getting HYPE

Next up, we need to grab the latest version of the HYPE framework from its home at www.hypeframework.org.

Once the download has completed, open the .zip file. You’ll find two SWC files named hype-framework.swc and hype-extended.swc. Copy both of these to your Base\lib folder.

These SWC files are essentially a collection of source files for the framework, all rolled in to one file for ease.

Copying the required SWC files

Step 3: Adding SWCS to Flash

The final step needed to get everything up and running is to tell Flash to look for the two new SWC files when we compile the movie, otherwise it will throw a whole bunch of errors our way, not nice!

Open the Main.fla inside the Base\src folder, then choose File, Publish Settings. Select the Flash tab as shown, in the new window that opens select the Library Path tab.

Adding a SWC to Flash

Press the “Browse TO SWC” File button and proceed to add both SWC files to the Flash file. Once this is complete it’s time to start adding some code!

Browsing for SWC files

Step 4: Init HYPE

Open up the Main.as source file in your chosen editor. Add the following private properties and the initHype() method:

public class Main extends MovieClip
{
	// private properties
	private var bitmapCanvas:BitmapCanvas;
	private var clipContainer:Sprite;
	private var objectPool:ObjectPool;

	/**
	 * constructor
	 */
	public function Main()
	{
		// inits the Hype framework
		initHype();
	}

	/**
	 * initiation of the Hype members we will be using and configuration prior
	 * to running the animation
	 */
	private function initHype():void
	{
		// the clipContainer is used as a parent for all of our objects
		clipContainer = new Sprite();
		addChild(clipContainer);
	}
}

In the next few steps we’ll be looking at each of these objects we’ve added as private properties, starting with the clipContainer Sprite.

As our design is going to have over a hundred objects all moving around the screen at once, we’re going to need something to house them all – just adding them to the Stage will become problematic further down the line. The answer is to create a regular AS3 Sprite to act as a parent.


Step 5: The BitmapCanvas

The first real part of HYPE, the BitmapCanvas can be thought of as a Sprite or better still, a Bitmap/BitmapData to which we will be painting our objects, each frame, like a painters canvas.

We create it just below the clipContainer code and define it with a width and height to match the Stage. We add it to the Stage but also tell it to startCapture(clipContainer, true), this simply tells the BitmapCanvas to take a snapshot of the clipContainer each frame. For now though, keep this commented out!

	/**
	 * initiation of the Hype members we will be using and configuration prior
	 * to running the animation
	 */
	private function initHype():void
	{
		// the clipContainer is used as a parent for all of our objects
		clipContainer = new Sprite();
		addChild(clipContainer);

		// think of the BitmapCanvas as an empty space we will 'paint'
		// every frame with new image data
		bitmapCanvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight);
		//bitmapCanvas.startCapture(clipContainer, true);
		addChild(bitmapCanvas);
	}
}

Step 6: The ObjectPool

If you have worked with games you’ve probably come across the concept of an ObjectPool. As creating new objects is too much of a hit on the processor we create a pool of a specified amount of objects before the game or application begins. We would then use the objects from this pool and upon running out we would recycle and reuse them all again, which avoids creating new objects. This is commonly used for bullets/lasers in games and the same logic is used in HYPE.

If you take a look at the Main.fla Library in Flash you will see I’ve created a MovieClip called circleShape and given it the Linkage Identifier of circleShape so we can create multiple copies of this object with code; this is what our ObjectPool will house.

a MovieClip to be used with ObjectPool

Add the ObjectPool below the BitmapCanvas code, like so:

	/**
	 * initiation of the Hype members we will be using and configuration prior
	 * to running the animation
	 */
	private function initHype():void
	{
		// the clipContainer is used as a parent for all of our objects
		clipContainer = new Sprite();
		addChild(clipContainer);

		// think of the BitmapCanvas as an empty space we will 'paint'
		// every frame with new image data
		bitmapCanvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight);
		//bitmapCanvas.startCapture(clipContainer, true);
		addChild(bitmapCanvas);

		// create a collection of 10 objects and store them in an ObjectPool
		// for use in the animation
		objectPool = new ObjectPool(circleShape, 10);
	}

Step 7: Creating Objects Using ObjectPool.request();

Now we have our core players setup, the clipContainer, the BitmapCanvas and the ObjectPool with its 10 clips, it’s time to start making things move.

To get an item from the ObjectPool we can use objectPool.request(); which will give us a circleShape MovieClip from the Flash Library to work with.

The ObjectPool also gives us the objectPool.onRequestObject() method which is a handy way to assign properties of a clip each time we request one. Add the following below where you instantiated the ObjectPool:

	// create a collection of 10 objects and store them in an ObjectPool
	// for use in the animation
	objectPool = new ObjectPool(circleShape, 10);

	// each time we request a new shape from the pool
	// it will perform the following
	objectPool.onRequestObject = function(clip:MovieClip):void
	{
		clip.x = Math.random() * stage.stageWidth;
		clip.y = Math.random() * stage.stageHeight;

		clipContainer.addChild(clip);
	}

Step 8: See the Result

With that new code added, every time we request an object from the pool by using objectPool.request() it will create a circleShape. Add it to the clipContainer and position it randomly on the screen. You can test this by amending the constructor to look like the following:

	/**
	 * constructor
	 */
	public function Main()
	{
		// inits the Hype framework
		initHype();

		objectPool.request();
	}

If all went well you should have a single, lonely circle on the screen.


Step 9: Requesting all Objects at Once

Do you remember we set the ObjectPool size to 10? Well we’re going to up the ante and increase this to 100 objects by changing the following:

	// create a collection of 10 objects and store them in an ObjectPool
	// for use in the animation
	objectPool = new ObjectPool(circleShape, 100);

We can amend the earlier request statement to read as requestAll() like this:

	/**
	 * constructor
	 */
	public function Main()
	{
		// inits the Hype framework
		initHype();

		objectPool.requestAll();
	}

Now we should have 100 circles scattered over the screen’s area.


Step 10: Adding FixedVibrations

Now we have our 100 circleShapes distributed around the screen it’s time to make our design come to life by adding movement.

Let’s start by applying a FixedVibration to the alpha and scale properties of each clip. We can use the ObjectPools onRequestObject method to implement it as shown:

	// create a collection of 10 objects and store them in an ObjectPool
	// for use in the animation
	objectPool = new ObjectPool(circleShape, 100);

	// each time we request a new shape from the pool
	// it will perform the following
	objectPool.onRequestObject = function(clip:MovieClip):void
	{
		clip.x = Math.random() * stage.stageWidth;
		clip.y = Math.random() * stage.stageHeight;

		// add a FixedVibration to the alpha and scale properties of each circleShape when requested
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
		alphaVib.start();
		scaleVib.start();

		clipContainer.addChild(clip);
	}

Let’s have a closer look at the FixedVibration objects we created. Each FixedVibration object we create takes 7 parameters, respectively they are as follows:

  • The object to apply the FixedVibration to, in our case our circleShape called “clip”.
  • The property to apply the FixedVibration to, this time we’re working with the alpha and scale properties.
  • The third parameter is the Spring of the FixedVibration, the higher the number the more ‘springy’ the movement. A value between 0 and 1 will work best.
  • Next up is the Ease of the vibration, the lower the value the quicker it will slide between the following two values. A value between 0 and 1 will work best.
  • The minimum value is up next, this will be the lowest the the vibration will hit.
  • Similarly, the maximum value will be the maximum value the vibration will hit.
  • Finally, the last parameter is relative, default this to false.

So how do all of these fit together? Let’s look at how the scale property is affected by the FixedVibration. It’s given Min and Max values of 0.05 and 0.8, the Spring value is pretty high at 0.9 and the Ease is pretty low at 0.05 making it scale erratically and fast.

Have a play around with these values to get a feel for how they influence the movement.

When we test our Flash file we should get something like this:


Step 11: Adding a VariableVibration

Very similar to the FixedVibration, the VariableVibration will adjust a property of an object with a value that fluctuates. The difference being that the VariableVibration isn’t linear as the name suggests.

Amend your code as follows to place the clips to the center of the Stage, only this time we’ll apply a VariableVibration to the x and y values to start seeing some movement!

	// each time we request a new shape from the pool
	// it will perform the following
	objectPool.onRequestObject = function(clip:MovieClip):void
	{
		clip.x = stage.stageWidth/2;
		clip.y = stage.stageHeight/2;

		// add a VariableVibration for the x/y movement of each circleShape
		var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
		var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
		xVib.start();
		yVib.start();

		// add a FixedVibration to the alpha and scale properties of each circleShape when requested
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
		alphaVib.start();
		scaleVib.start();

		clipContainer.addChild(clip);
	}

Let’s have a closer look at the VariableVibration objects we created. Each VariableVibration object we create takes only 5 parameters, respectively they are as follows:

  • The object to apply the VariableVibration to, in our case our circleShape called “clip”.
  • The property to apply the VariableVibration to, this time we’re working with the x and y properties.
  • The third parameter is the Spring of the vibration.
  • Next up is the Ease of the vibration.
  • The final parameter is the Range of values that is produced. The higher the number the more erratic the effect.

Our Flash file should look something like this when published:


Step 12: Adding Some Wow

It’s starting to look good, but we can do much better! Remember that bitmapCanvas.startCapture() line I asked you to keep uncommented way back in Step 6? Go ahead and uncomment it then test your movie again.

This is more like it!


Step 13: A Quick Trick for Rotation

A very simple trick to add a spiraling movement is to add another vibration to the clip’s rotation property like so:

	// each time we request a new shape from the pool
	// it will perform the following
	objectPool.onRequestObject = function(clip:MovieClip):void
	{
		clip.x = stage.stageWidth/2;
		clip.y = stage.stageHeight/2;

		// add a VariableVibration for the x/y movement of each circleShape
		var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
		var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
		xVib.start();
		yVib.start();

		// add a FixedVibration to the alpha and scale properties of each circleShape when requested
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
		alphaVib.start();
		scaleVib.start();

		// add a FixedVibration to the rotation of the circleShape
		var rotationVib:FixedVibration = new FixedVibration(clip, "rotation", 0.9, 0.05, 0, 360, false);
		rotationVib.start();

		clipContainer.addChild(clip);
	}

Step 14: A Quick Trick for Rotation

Before testing this jump over to Flash and open the circleShape MovieClip in the Library.

As shown, drag the circle just off from center in any direction. The further you move it from center, the more spirals will appear in your design when you publish:

Offsetting the circleShape for spirals

Step 15: ExitShapes

One problem with our current animation is that once the clips move off the screen, they very rarely come back. We can solve this little problem by creating an ExitShapeTrigger.

An ExitShapeTrigger is an area considered safe for the clip. When the clip leaves this area we need to perform some kind of function, such as requesting a new clip from the ObjectPool.

We define an ExitShapeTrigger as below:

	// each time we request a new shape from the pool
	// it will perform the following
	objectPool.onRequestObject = function(clip:MovieClip):void
	{
		clip.x = stage.stageWidth/2;
		clip.y = stage.stageHeight/2;

		// add a VariableVibration for the x/y movement of each circleShape
		var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
		var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
		xVib.start();
		yVib.start();

		// add a FixedVibration to the alpha and scale properties of each circleShape when requested
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
		alphaVib.start();
		scaleVib.start();

		// add a FixedVibration to the rotation of the circleShape
		var rotationVib:FixedVibration = new FixedVibration(clip, "rotation", 0.9, 0.05, 0, 360, false);
		rotationVib.start();

		// define an ExitShape
		var exit:ExitShapeTrigger = new ExitShapeTrigger(onExitShape, clip, exitShape, true);
		exit.start();

		clipContainer.addChild(clip);
	}

	// recycle objects
	private function onExitShape(clip:MovieClip):void
	{
		trace("circleShape left the screen!");
	}

This will create an ExitShapeTrigger with the following parameters:

  • The method to trigger when the event has occurred.
  • The MovieClip to test if it is out of the given area.
  • The MovieClip used to define the safe area, you might have already noticed we’ve already created this, called it exitShape and placed it on the Stage in Flash.
  • The last parameter is the Enter Once flag, just set this to true for now.

Step 16: ObjectPool Release

Following on from the ExitShape we introduced in the last step, we’re going to simply edit the onExitShape method so that whenever a clip moves off the screen, we’ll delete it and request a new one from the ObjectPool.

Until now we’ve been working with the request() and requestAll() methods of the ObjectPool, when we want to delete the old one we use the release(clip) method:

	// recycle objects
	private function onExitShape(clip:MovieClip):void
	{
		// remove from the ObjectPool and the clipContainer
		objectPool.release(clip);
		clipContainer.removeChild(clip);

		// get a new clip from the ObjectPool
		objectPool.request();
	}

Step 17: Adding a Blur

HYPE features the ability to very easily add filters such as blur and glow to objects. To add a touch more pizzazz to the design we’re going to add a BlurFilter to the project using the FilterCanvasRhythm:

	// think of the BitmapCanvas as an empty space we will 'paint'
	// every frame with new image data
	bitmapCanvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight);
	bitmapCanvas.startCapture(clipContainer, true);
	addChild(bitmapCanvas);

	// adding a blur
	var blur:FilterCanvasRhythm = new FilterCanvasRhythm([new BlurFilter(1.1, 1.1, 1)], bitmapCanvas);
	blur.start(TimeType.TIME, 100);

	// create a collection of objects and store them in an ObjectPool
	// for use in the animation
	objectPool = new ObjectPool(circleShape, 100);

The above code creates a FilterCanvasRhythm which takes a Filter as a parameter and applies it to the bitmapCanvas we declared earlier.

Test the project, it’s really starting to come together now!


Step 18: Adding Some Diversity

We can easily add a little depth to the composition by adding more shapes in to the mix. Rather than having to create and manage several ObjectPools, we can add frames to the original circleShape we used and randomly select which frame to play.

Try this now, edit the circleShape object in the Flash Library. Create a new Keyframe, select a new color and draw a new shape. Go ahead and create a few Keyframes of your own, in the next step we’ll look at implementing them with code. This is our new shape:

Offsetting the circleShape for spirals

..compared with our old shape:

Offsetting the circleShape for spirals

Step 19: Choosing Random Frames for circleShape

With our circleShape now sporting a few new Keyframes we can simply insert this single line of code to choose a frame to use each time we call objectPool.request():

	// each time we request a new shape from the pool
	// it will perform the following
	objectPool.onRequestObject = function(clip:MovieClip):void
	{
		clip.x = stage.stageWidth/2;
		clip.y = stage.stageHeight/2;

		// choose a random frame
		clip.gotoAndStop(Math.ceil(Math.random() * 3));

		// add a VariableVibration for the x/y movement of each circleShape
		var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
		var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
		xVib.start();
		yVib.start();

		// add a FixedVibration to the alpha and scale properties of each circleShape when requested
		var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
		var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
		alphaVib.start();
		scaleVib.start();

		// add a FixedVibration to the rotation of the circleShape
		var rotationVib:FixedVibration = new FixedVibration(clip, "rotation", 0.9, 0.05, 0, 360, false);
		rotationVib.start();

		// define an ExitShape
		var exit:ExitShapeTrigger = new ExitShapeTrigger(onExitShape, clip, exitShape, true);
		exit.start();

		clipContainer.addChild(clip);
	}

As a quick note, the random frame code above will switch between frames 1, 2 and 3. You may need to adjust this to the amount of frames in your circleShape.


Step 20: Finish

It’s time to test your movie and bask in the mixture of funky patterns and colors as the HYPE framework takes your code and mixes it into a piece of generative art.

Heres the final code for reference:

package
{
	import hype.extended.behavior.FixedVibration;

	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.filters.BlurFilter;
	import hype.extended.behavior.VariableVibration;
	import hype.extended.rhythm.FilterCanvasRhythm;
	import hype.extended.trigger.ExitShapeTrigger;
	import hype.framework.core.ObjectPool;
	import hype.framework.core.TimeType;
	import hype.framework.display.BitmapCanvas;

	/**
	 * A tutorial to introduce HYPE. A visual framework
	 * by Branden Hall and Joshua David for creating
	 * generative / iterative design with code.
	 *
	 * @author Anton Mills
	 * @version 06/02/2011
	 */
	public class Main extends MovieClip
	{
		// private properties
		private var bitmapCanvas:BitmapCanvas;
		private var clipContainer:Sprite;
		private var objectPool:ObjectPool;

		/**
		 * constructor
		 */
		public function Main()
		{
			// inits the Hype framework
			initHype();

			// begin sequence
			objectPool.requestAll();
		}

		/**
		 * initiation of the Hype members we will be using and configuration prior
		 * to running the animation
		 */
		private function initHype():void
		{
			// the clipContainer is used as a parent for all of our sprites
			clipContainer = new Sprite();
			addChild(clipContainer);

			// think of the BitmapCanvas as an empty space we will 'paint'
			// every frame with new image data
			bitmapCanvas = new BitmapCanvas(stage.stageWidth, stage.stageHeight);
			bitmapCanvas.startCapture(clipContainer, true);
			addChild(bitmapCanvas);

			// adding a blur
			var blur:FilterCanvasRhythm = new FilterCanvasRhythm([new BlurFilter(1.1, 1.1, 1)], bitmapCanvas);
			blur.start(TimeType.TIME, 100);

			// create a collection of objects and store them in an ObjectPool
			// for use in the animation
			objectPool = new ObjectPool(circleShape, 100);

			// each time we request a new shape from the pool
			// it will perform the following
			objectPool.onRequestObject = function(clip:MovieClip):void
			{
				clip.x = stage.stageWidth/2;
				clip.y = stage.stageHeight/2;

				// choose a random frame
				clip.gotoAndStop(Math.ceil(Math.random() * 3));

				// add a VariableVibration for the x/y movement of each circleShape
				var xVib:VariableVibration = new VariableVibration(clip, "x", 0.97, 0.03, 40);
				var yVib:VariableVibration = new VariableVibration(clip, "y", 0.97, 0.03, 40);
				xVib.start();
				yVib.start();

				// add a FixedVibration to the alpha and scale properties of each circleShape when requested
				var alphaVib:FixedVibration = new FixedVibration(clip, "alpha", 0.9, 0.05, 0.5, 1.5, false);
				var scaleVib:FixedVibration = new FixedVibration(clip, "scale", 0.9, 0.05, 0.05, 0.8, false);
				alphaVib.start();
				scaleVib.start();

				// add a FixedVibration to the rotation of the circleShape
				var rotationVib:FixedVibration = new FixedVibration(clip, "rotation", 0.9, 0.05, 0, 360, false);
				rotationVib.start();

				// define an ExitShape
				var exit:ExitShapeTrigger = new ExitShapeTrigger(onExitShape, clip, exitShape, true);
				exit.start();

				clipContainer.addChild(clip);
			};
		}

		// recycle objects
		private function onExitShape(clip:MovieClip):void
		{
			objectPool.release(clip);
			clipContainer.removeChild(clip);

			objectPool.request();
		}
	}
}

Conclusion

This just about wraps up introducing the HYPE framework, thanks very much for your time. I hope you enjoyed it and remember we only scratched the surface of some of the fantastic effects that can be made with the framework. Please do check out the HYPE framework website at www.hypeframework.org for some fantastic examples of the framework and how others have taken it to the next level with Away3D/Papervision integration!


More HYPE Resources on Activetuts+

  • Code a Chaotic Composition Inspired by Joshua Davis by Bruno Crociquia
  • Introduction to the HYPE ActionScript 3.0 Framework by Tom Green
  • Quick Tip: How to Export Flash to PNG with HYPE by Daniel Apt
  • Create a Mesmeric Music Visualizer with HYPE by Franci Zidar



View full post on Activetuts+

Jan 1, 2011 Posted on Jan 1, 2011 in Flash Video Training | 24 comments

Creating Smooth Animations in Flash


How to create smooth animations in flash.

Dec 23, 2010 Posted on Dec 23, 2010 in Hints and Tips | 0 comments

Creating Realistic 3D Animation in Flash CS4

Flash CS4 came with some big changes and features that still amaze us even after the more recent release of Flash CS5. Flash now has the ability to work with objects in 3D space directly within the working environment.

Although it’s still a basic feature compared to what you can do with 3D in ActionScript classes, this tool opens the door to designers and animators who don’t know much about code. It allows them to start moving their objects in 3D space without the need to use old methods (such as importing 3D animation in sequence images etc.)

Along with the 3D tool, Flash has changed the way we understand motion tweening by adding the new motion tweening style, which is more like the animation auto-key in After Effects. Along with the motion tweening changes, Flash comes with the Motion Editor. This panel has a lot of features and capabilities giving the animator more control over animation in Flash using animation curves.

The Motion Editor panel not only enhances animation by giving more options through curves control, it also provides control over animation easing, lending a more realistic look. The easing concept is known in many animation applications such as After Effects, but it was previously so basic in Flash and limited to changing the easing value from 100 to -100. The newer easing features give you more ability to control animation easing as we will see in the steps below.

In this tutorial, we’ll merge the 3D and easing features by creating 3D animation in Flash and also applying the easing curves.


Final Result Preview

Let’s take a look at the final result we will be working towards:

The first part in our tutorial is to build the cartoon scene for a room with a door.


Step 1: New Document

Open a new Flash document with the dimensions 600px X 450px and start drawing the outline of the room demonstrated in the figure below.

Note that the 3D tool is only available when you have the document publish settings for ActionScript 3 and Flash Player 10 or later. Additionally, it can be applied only on movie clip symbols. You should make sure that your File Publish Settings (found in File > Publish Settings, surprisingly) are set to Action Script 3 and Flash Player 10 or higher.


Step 2: Door

Cut the door area and place it on a new layer to be able to control it using the 3D tools.


Step 3: Details

Now, let’s add some details to the background to give it a real cartoon look. We’ll also add some shadows and textures to the floor and door.


Step 4: “Door”

Convert the door symbol to a movie clip and give it the name ‘Door’.


Step 5: The 3D Vavigation Asset

The 3D navigation asset includes four cycles, dragging over each cycle allows you to rotate the symbol in one of the directions as following:

  • The red cycle lets you rotate the symbol on the X axis
  • The green cycle lets you rotate the symbol on the Y axis
  • The blue cycle lets you rotate the symbol on the Z axis
  • Dragging over the orange cycle lets you rotate the object in any of these directions. While it provides free rotation, it does not provide accurate rotation values.

While the door symbol is selected, choose the 3D tool. The 3D asset cycles appear in the middle of the movie clip symbol.


Step 6: Point of Rotation

Click on the center point of the 3D cycles and drag the navigation asset to the middle right of the door to make it the center point where the door rotates around. The final look for the background should be as below.


Step 7: Timeline

In the above steps, we prepared the cartoon background for the 3D animation through motion tweening. In this animation, we will animate the door to open and close using the 3D tool. Drag the timeline indicator to the first frame in the Timeline panel to start the animation.


Step 8: Select

Click on the 3D Rotate tool and click on the door symbol. Notice that the 3D navigation asset appears on the right side of the object.


Step 9: Open Sesame

Click over the green cycle and drag to open the door in the direction outside the room as shown in the figure below.


Step 10: Rotate

Move the timeline indicator to frame 50. And use the 3D Rotate tool to rotate the door to the other side of the room as shown in the figure below.

We’ve now created the 3D animation for the door on the Flash stage. Next, we will use the motion editor to create more realistic animation effects using the Easing curves in the Motion Editor panel. Whilst this enhanced easing feature was added to Flash CS4 and newer releases, the older versions of Flash had a simplified easing feature through the Easing value in the properties panel. You can still find this value when you click anywhere in motion tweening, but this method never gave many options other than easing in and out.

The Motion Editor panel consists of animation curves that control the object properties through animation over the timeframe. Each curve represents a specific property for the object and is responsible for it changing over the time and the level of the change. For example, the X curve represents the animation of the object on the X axis. This value becomes active when you create a motion tweening associated with it.

The easing curves in the Motion panel are similar to the rest of the motion editor animation curves as they gives you more ability to control the animations’ realistic motion.

There are two main methods to create the easing curve. The first one is through the Ease presets drop-down menu that includes ready easing options. The second method is to create the custom easing curve. In this tutorial we will try both methods.


Before we proceed, here’s a brief overview of the easing curves in the Motion panel.

In the Easing part of the Motion Editor panel, you can add / remove and edit the easing curves by simply selecting the desired curve. Before we see how to apply the easing effect on the 3D animation, we will briefly see how the Easing curve works in the Motion Editor panel.

The easing curve is a comprehensive method to control how the animation accelerates through the motion tweening to create a more realistic animation; it shows the acceleration or deceleration value of the animation through the time.

The Motion Editor panel gives you the option to create many easing curves and assign different curves to each motion property and apply different easings to each feature. In the following steps, we will start by applying an easing preset to the door opening animation.


Step 11: Select

Select the movie clip or click anywhere on the motion tweening, go to the Motion Editor panel and navigate to the Ease section.


Step 12: Add Ease

Click the plus icon in the Ease section to add ease from the drop-down easing presets. Choose the Spring easing.


Step 13: Y Curve

Go to the Rotation Y curve that displays the animation of the door. In the Easing drop-down list, choose Spring. Press Ctrl+Enter to test the animation.

You will notice that the door animation changed from a simple animation of two keyframes to a complex animation with spring effect. Also, you can add other easing presets and try them to see how it affects the door animation. The animation should be as following:

In the following steps, we will create a custom easing curve and apply it to the door opening animation. We’ll have a closer look at how the curve works and get to grips with how to work with it.


Step 14: Remove Easing

First, let us remove the previous easing either by selecting No Ease from the easing drop-down list or clicking the minus icon on the easing section and choosing the easing you would like to remove. In this example, we will remove the Spring easing.


Step 15: Custom Easing

Click the plus icon next to the Ease section and choose Custom from the drop-down list. This will create a new custom easing curve with a green curve to indicate it is editable. If you create additional custom easing curves, each curve will have a unique color associated with it.


Step 16: Handles

The ease curve works similarly to drawing paths. You can create points on it; these points represent the keyframes or the points that indicate changes in the easing animation. Each point has two handles to change how the right and left parts of the curve look. Now, we will create points on the easing curve that will affect the door animation:

Right click on the easing curve and create keyframes on frames 10, 20, 30 and 40


Step 17: 0 – 100

The easing curve ranges between zero and 100. When the curve moves towards the zero value, the animation moves toward the first frame and moving the curve to the 100 value means it moves to the end of the animation. Also, note that the hard slip curve means higher speed acceleration and slow slip means slow animation acceleration.

We’ll edit the points’ position and curve to affect the animation acceleration. Select the first point and move it to easing value 100. This means that the animation will reach its end at this point and the hard slip means that the animation will accelerate fast.


Step 18: Points

Move the second point to the value zero, the third point to value 50 and the fourth point to value 50.


Step 19: Smooooth

Use the point handlers on the last two points to create a smooth curved slip to make the door animate slow at the end of the animation. The final easing curve should like the figure below:


Step 20: Test

Press Ctrl+Enter to test the animation and see how the new easing curve affects the door animation. The final animation for the custom easing should be as below:


Conclusion

This is the end of the tutorial. It aimed to help you understand how to use Flash’s 3D tools to create animation in 3D space and apply the comprehensive easing features in the Motion panel through the easing curves.

Understanding Easing curves can help you create complex, realistic animation more easily and efficiently by applying easing curves to the animation instead of creating many keyframes in motion tweening.

You will also find this video tutorial by Tom Green useful in understanding more about the Motion Editor in Flash: Flash CS4 Motion Editor.



View full post on Activetuts+

Dec 13, 2010 Posted on Dec 13, 2010 in Hints and Tips | 1 comment

Quick Tip: Creating a Snapshot Tool in Flash

In this Quick Tip, I’ll show you how to create a Shapshot Tool that copies part of the stage and saves the result as a PNG image.


Final Result Preview

This is the final result. Just click the stage and then drag the mouse to take a snapshot.


Step 1: Download the as3corelib Class

Create a new folder for this project and give it any name you want. Go to Github and download the latest version of the as3corelib Class. For this Quick Tip I’ve used version .93. Extract the ZIP file and go to as3corelib-.93 > src.

Copy the com directory to your newly created folder. This package has a very useful PNGEncoder Class that we will use for encoding the
snapshot into a PNG image.


Step 2: Setup your Flash File

Launch Flash and create a new Flash Document. Make sure it’s set to publish to Actionscript 3.0 and Flash Player 10. You can check this in the Properties
panel or by selecting Publish Settings… and then clicking on the Flash tab.

The Flash Properties Panel

Step 3: Content to be Snapped

We need some content in the Flash file to check if the snapshot tool is working properly. We’re going to create some circles and place them randomly around the stage. Create a new Document Class called Circles.as and add the following code. Remember to link the Class to the Flash file by writing Circles in the Class field in the the Properties Panel.

package
{
	import flash.display.Shape;
	import flash.display.Sprite;

	public class Circles extends Sprite
	{
		private var _circleCount:int = 20;

		public function Circles()
		{
			makeCircles();
		}

		private function makeCircles():void
		{
			for (var i:int = 0; i < _circleCount; i++)
			{
				var circle:Shape = new Shape();
				circle.graphics.lineStyle(1, 0xCCCCCC, 1);
				circle.graphics.beginFill(0x333333, 0.5);
				circle.graphics.drawCircle(Math.random() * stage.stageWidth, Math.random() * stage.stageHeight, (Math.random() * 40) + 20);
				circle.graphics.endFill();
				addChild(circle);
			}
		}
	}
}

Our snapshot function will work with any stage contents, though, so don’t feel that you have to restrict yourself to simple shapes!


Step 4: Create the SnapShot Class

Create a new Class file and give it a name of SnapShot.as. This is the Class that will hold all the methods used to take a snapshot. Add the following code to the Class.

package
{
	import flash.display.Stage;

	public class SnapShot
	{
		private var _stage:Stage;

		public function Snapshot() {}

		public function activate(stage:Stage):void
		{
			_stage = stage;
		}
	}
}

Add the following lines of code to the Circles Class. We use the activate() method to pass a reference to the Stage along to the SnapShot Class. We do this to so we can access the content on the Stage.

package
{
	import flash.display.Shape;
	import flash.display.Sprite;

	public class Circles extends Sprite
	{
		private var _circleCount:int = 20;
		private var _snapshot:SnapShot;

		public function Circles()
		{
			makeCircles();
			_snapshot = new SnapShot();
			_snapshot.activate(stage);
		}

		private function makeCircles():void
		{
			for (var i:int = 0; i < _circleCount; i++)
			{
				var circle:Shape = new Shape();
				circle.graphics.lineStyle(1, 0xCCCCCC, 1);
				circle.graphics.beginFill(0x333333, 0.5);
				circle.graphics.drawCircle(Math.random() * stage.stageWidth, Math.random() * stage.stageHeight, (Math.random() * 40) + 20);
				circle.graphics.endFill();
				addChild(circle);
			}
		}
	}
}

Step 5: Drawing the Boundaries

Expand the SnapShot Class to include the following methods. These methods are used to draw the boundaries frame, which allows users to select which part of the stage will be copied to the snapshot.

package
{
	import flash.display.Shape;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;

	public class SnapShot
	{
		private var _stage:Stage;
		private var _boundary:Shape;
		private var _originX:int;
		private var _originY:int;
		private var _mouseX:int;
		private var _mouseY:int;

		public function SnapShot() {}

		public function activate(stage:Stage):void
		{
			_stage = stage;
			addMouseListeners();
		}

		private function addMouseListeners():void
		{
			_stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			_stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
		}

		private function onMouseDown(e:MouseEvent):void
		{
			_stage.addEventListener(MouseEvent.MOUSE_MOVE, drawBoundaries);

			//These values are used later as a starting point for the boundary.
			_originX = _stage.mouseX;
			_originY = _stage.mouseY;
		}

		private function drawBoundaries(e:MouseEvent):void
		{
			if (_boundary == null) { _boundary = new Shape(); } 

			clearBoundaries()

			//This is to make the mouseY value stay within the limits of the Stage.
			_mouseY = Math.max(Math.min(_stage.mouseY,_stage.stageHeight), 0);
			//This is to make the mouseX value stay within the limits of the Stage.
			_mouseX = Math.max(Math.min(_stage.mouseX,_stage.stageWidth), 0);

			_boundary.graphics.lineStyle(2, 0x0, 0.5);
			_boundary.graphics.drawRect(_originX, _originY, _mouseX - _originX, _mouseY - _originY);
			_boundary.graphics.lineStyle(4, 0x0, 0.2);
			//This code makes sure we always draw from the top left point to the bottom right.
			_boundary.graphics.drawRect(Math.min(_originX, _mouseX) - 3, Math.min(_originY, _mouseY) - 3, Math.abs(_mouseX - _originX) + 6, Math.abs(_mouseY - _originY) + 6);
			_stage.addChild(_boundary);
		}

		private function clearBoundaries():void
		{
			_boundary.graphics.clear();
		}

		private function onMouseUp(e:Event):void
		{
			_stage.removeEventListener(MouseEvent.MOUSE_MOVE, drawBoundaries);
			clearBoundaries();
		}
	}
}

We start by checking if the user has clicked on the Stage. When he clicks on the stage we start running the drawBoundaries() method whenever the mouse is moved. This method draws the boundaries; whatever falls within the thin black line will be part of the snapshot. When the user releases the mouse we stop checking for mouse movements.

Step 6: Stage Content to Bitmap

Import the BitmapData and Matrix Classes and add the _content property to the list of private properties.

package
{
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;

	public class SnapShot
	{
		private var _stage:Stage;
		private var _boundary:Shape;
		private var _content:BitmapData;
		private var _originX:int;
		private var _originY:int;
		private var _mouseX:int;
		private var _mouseY:int;

Add the following code to the bottom of the Class:

private function onMouseUp(e:Event):void
{
	_stage.removeEventListener(MouseEvent.MOUSE_MOVE, drawBoundaries);
	clearBoundaries();
	createBitmap();
}

private function createBitmap():void
{
	//We add the -2 to offset for the boundary line.
	_content = new BitmapData(Math.abs(_mouseX - _originX) - 2, Math.abs(_mouseY - _originY) - 2);
	//The -1 is added for the same reason, to keep the line from appearing in the final image.
	var bitmapMatrix:Matrix = new Matrix(1, 0, 0, 1, -Math.min(_originX, _mouseX) - 1, -Math.min(_originY, _mouseY) - 1);
	_content.draw(_stage, bitmapMatrix);
}

The createBitmap() method creates a new BitmapData object with the width and the height of the content area inside the boundary. The matrix variable transitions (moves) the image so that when the draw() method is called it starts copying from the top left corner of the boundary area.

Step 7: Saving the Bitmap

To save the bitmap we need to import several Classes.

  • The ByteArray Class is used to encode the BitmapData object.
  • The PNGEncoder Class is used to convert the encoded data into a PNG Image.
  • The FileReference Class is used to save the image to the users hard drive.

We’ve also added an _imageCount property which we use to increment the image names.

package
{
	import com.adobe.images.PNGEncoder;
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.net.FileReference;
	import flash.utils.ByteArray;

	public class Snapshot
	{
		private var _stage:Stage;
		private var _boundary:Shape;
		private var _content:BitmapData;
		private var _originX:int;
		private var _originY:int;
		private var _mouseX:int;
		private var _mouseY:int;

		private var _imageCount:int = 1;

Add the following code to the bottom of the SnapShot Class:

private function createBitmap():void
{
	_content = new BitmapData(Math.abs(_mouseX - _originX) - 2, Math.abs(_mouseY - _originY) - 2);
	var bitmapMatrix:Matrix = new Matrix(1, 0, 0, 1, -Math.min(_originX, _mouseX) - 1, -Math.min(_originY, _mouseY) - 1);
	_content.draw(_stage, bitmapMatrix);

	saveBitmap();
}

private function saveBitmap():void
{
	var encodedContent:ByteArray = PNGEncoder.encode(_content);
	var fileWindow:FileReference = new FileReference();
	fileWindow.save(encodedContent, "Image_" + _imageCount + ".png");
	_imageCount++;
}

The saveBitmap method is fairly easy to understand. We encode the BitmapData object and save it to the user’s hard drive.
We use the _imageCount property to make it easier for user to save several images in a row.

Conclusion

The Snapshot Tool is now complete and with just three lines of code can be implemented into any project.

Make sure you’re running Flash Player 10 and that you have the as3corelib package in the correct directory.

I hope you enjoyed this Quick Tip, thank you for reading!



View full post on Activetuts+

Page 1 of 612345...»Last »
search search search search search
Find an Article
Categories
  • Flash Video Training
  • Hints and Tips
  • Recommended
Please Support Our Sponsors
Recent Posts
  • The Math and ActionScript of Curves: Drawing Quadratic and Cubic Curves
  • Weekend Lecture: Understanding Games, a Flash Game About Game Design
  • Weekend Lecture: Understanding Games, a Flash Game About Game Design
  • Workshop Coding Challenge: Fix This Breakout Game
  • Enable the Latest AIR SDK in Flash Professional CS5.5+
Tag Cloud
2011 ActionScript Active Activetuts+ Adobe animation Basic Basix Best Build Button Character Create Creating Critique Custom design Effect Effects Files Flash from Game Guide HTML5 Introduction Macromedia Motion Muzzle part Player Premium Professional Quick Silverlight Simple Text Tool Tutorial Tuts+ Tween Using Video website Workshop
About Our Site:

Hey there and welcome to "Flash Video Training Source", a resource for anybody interested in learning more about Adobe's great tool. We feature educational videos, which will help you master Adobe Flash and help you get to know all of its features. We at "Flash Video Training Source" believe that video training and video... more

Why don't you follow us on Twitter and get the latest video tutorials twitted to your account. Just click on the floating twitter bar to your right!

Go Back In Time
May 2012
M T W T F S S
« Apr    
 123456
78910111213
14151617181920
21222324252627
28293031  
Pretty Blank Box
top

Blogroll

  • Development Blog
  • Documentation
  • Plugins
  • Suggest Ideas
  • Support Forum
  • Themes
  • WordPress Planet

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org

Archives

  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • May 2010
  • April 2010
Powered by WordPress  |  Designed by Elegant Themes  |  Lightning Fast Hosting by Site 5 Hosting