Read Foundation Game Design with ActionScript 3.0, Second Edition Online
Authors: Rex van der Spuy
if(event.keyCode == Keyboard.SPACE)
{
if(!_star.launched)
{
_star.x = _character.x + _character.width / 2;
_star.y = _character.y + _character.width / 2;
_star.launched = true;
}
}
}
private function keyUpHandler(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.LEFT
|| event.keyCode == Keyboard.RIGHT)
{
_character.vx = 0;
}
else if (event.keyCode == Keyboard.DOWN
|| event.keyCode == Keyboard.UP)
{
_character.vy = 0;
}
}
private function addGameObjectToLevel
(gameObject:Sprite, xPos:int, yPos:int):void
{
this.addChild(gameObject);
gameObject.x = xPos;
gameObject.y = yPos;
}
}
}
To simplify the code a bit, I've declared all the variables and objects you need for scrolling as instance variables in the class definition. This means they can be accessed anywhere in the class by any method or object whenever they're needed.
private var _temporaryX:int;
private var _temporaryY:int;
private var _scroll_Vx:int;
private var _scroll_Vy:int;
private var _rightInnerBoundary:uint;
private var _leftInnerBoundary:uint;
private var _topInnerBoundary:uint;
private var _bottomInnerBoundary:uint;
private var _currentExplosion:Sprite = null;
ThestartGame
method then initializes the inner boundary variables.
_rightInnerBoundary
= (_stage.stageWidth / 2) + (_stage.stageWidth / 4);
_leftInnerBoundary
= (_stage.stageWidth / 2) - (_stage.stageWidth / 4);
_topInnerBoundary
= (_stage.stageHeight / 2) - (_stage.stageHeight / 4);
_bottomInnerBoundary
= (_stage.stageHeight / 2) + (_stage.stageHeight / 4);
TheenterFrameHandler
calculates the scroll velocity by first capturing the background's position in temporary variables, moving the background, and then calculating the scroll velocity.
_temporaryX = _background.x;
_temporaryY = _background.y;
//...scroll the background...
_scroll_Vx = _background.x - _temporaryX;
_scroll_Vy = _background.y - _temporaryY;
With the_scroll_Vx
and_scroll_Vy
variables now in the bag, you can scroll the game objects using thescroll
method.
//Scroll the monsters
scroll(_monster1);
scroll(_monster2);
scroll(_monster3);
scroll(_monster4);
//Scroll the star
if(_star.launched)
{
scroll(_star);
}
Here's thescroll
method that does this work:
public function scroll(gameObject:Sprite):void
{
gameObject.x += _scroll_Vx;
gameObject.y += _scroll_Vy;
}
Because the game world is now so much bigger, thecheckStageBoundaries
method needs to compensate for this. Here's the updatedcheckStageBoundaries
method that makes sure the game objects don't cross the edges of the playing field:
private function checkStageBoundaries(gameObject:Sprite):void
{
if (gameObject.x < _background.x + 50)
{
gameObject.x = _background.x + 50;
}
if (gameObject.y < _background.y + 50)
{
gameObject.y = _background.y + 50;
}
if (gameObject.x + gameObject.width
> _background.x + _background.width - 50)
{
gameObject.x
= _background.x + _background.width - gameObject.width - 50;
}
if (gameObject.y + gameObject.height
> _background.y + _background.height - 50)
{
gameObject.y
= _background.y + _background.height - gameObject.height - 50;
}
}
ThecheckStarStageBoundaries
method is also modified to let the star move to the edge of the stage boundaries.
private function checkStarStageBoundaries(star:Star):void
{
if (star.y < 0
|| star.x < 0
|| star.x > _stage.stageWidth
|| star.y > _stage.stageHeight)
{
_star.launched = false;
}
}
As you can see, all this code just compensates for the larger game world and adds the scroll velocity to the game objects' velocities.
There's one exception that you need to take a special look at: how to scroll the explosions.
All the explosion objects are created locally in thekillMonster
method, like this:
var explosion:Explosion = new Explosion();
this.addChild(explosion);
That means they can't be accessed outside thekillMonster
method. This is a problem because theenterFrameHandler
needs some sort of access to them so that they can be scrolled after they're added to the stage.
A simple solution to this is to create an instance variable that stores a reference to the current explosion. You'll recall that instance variables are any variables declared in the class definition. They're available to
all objects and all methods anywhere in the class. I created an instance variable called_currentExplosion
in the class definition and gave it an initial value of null.
private var _currentExplosion:Sprite = null;
It's a Sprite, so it can contain any objects that extend the Sprite class, just as the explosion objects do.
When thekillMonster
class creates a temporary explosion object, all you need to do is copy it into the_currentExplosion
variable, like this:
var explosion:Explosion = new Explosion();
this.addChild(explosion);
_currentExplosion = explosion;
This lets you hold a reference to the temporary explosion that can be used in other methods in the class. TheenterFrameHandler
can use this_currentExplosion
object to scroll the explosion, like this:
if(_currentExplosion != null)
{
scroll(_currentExplosion);
}
This technique is a quick and easy way to reference temporary objects and use them outside of the method where they were created. But it might not be the best way. It starts to become complicated to track and control temporary objects, especially if you need to access more than one at a time. In
Chapter 10
you'll learn how to use loops and arrays to precisely add, remove, and control temporary objects in a game.
It's very easy to make the monsters actively hunt the character. All the code needs to do is figure out whether a monster is above, below, to the left, or to the right of the character. Once you know that, just change the monster's velocity so that it moves towards the player. The code you need to write follows this logic.
if(the character is to the left of the monster)
{
move the monster to the left
}
Here's what the actual code could look like in Monster Mayhem:
if(_character.x < monster.x)
{
monster.vx = -2;
monster.vy = 0;
}
As you can see, it's just a simple matter of comparing the x position of the character to the x position of the monster and then moving the monster to the left.
You can see all this code in action in the IntelligentMonsters example in the project's source files. Run the SWF and you'll notice that the monsters chase the character around the stage. Every second they decide whether they should move closer to the character by going left or right, or by going up or down. If you stop moving the character, you'll notice the monsters swarm around it.
Figure 8-23
illustrates the monsters' behavior.
Figure 8-23.
Monsters that chase the player
The code that does this is extremely simple. All it requires is a small change to thechangeMonsterDirection
method. Here's the updated method:
private function changeMonsterDirection(monster:Monster):void
{
//Choose a random number between 1 and 2
var randomNumber:int = Math.ceil(Math.random() * 2);
//If the number is 1, move closer to the character
//on the horizontal axis
if(randomNumber == 1)
{
if(_character.x > monster.x)
{
//Right
monster.vx = 2;
monster.vy = 0;
}
if(_character.x < monster.x)
{
//Left
monster.vx = -2;
monster.vy = 0;
}
}
//If the number is 2, move closer to the character
//on the vertical axis
if(randomNumber == 2)
{
if(_character.y < monster.y)
{
//Up
monster.vx = 0;
monster.vy = -2;
}
if(_character.y > monster.y)
{
//Down
monster.vx = 0;
monster.vy = 2;
}
}
}
The code first chooses a random number between 1 and 2.
var randomNumber:int = Math.ceil(Math.random() * 2);
If the number is 1, the monster decides to move closer to the character on the x axis.
if(randomNumber == 1)
{
//Move left or right...
}
If the random number is 2, the monster moves closer to the character on the y axis.
if(randomNumber == 2)
{
//Move up or down...
}
I've added this bit of randomness to make the game slightly easier to play. You could easily make the monsters very precise in their direction choices by making them move only in the direction that is closest to the character. To do this, compare the distance between the x and y positions and have the monster choose whichever distance is the longest. Use theMath.abs
method to find out what the distances are without having to worry about whether the x and y positions of the objects are positive or negative.
//Find out whether the monster is closer to the
//character on the x axis or the y axis
var vx:Number = Math.abs(_character.x - monster.x);
var vy:Number = Math.abs(_character.y - monster.y);
//Move the character right or left if it's
//already closer on the y axis
if(vx>vy)
{
if(_character.x > monster.x)
{
//Right
monster.vx = 2;
monster.vy = 0;
}
else
{
//Left
monster.vx = -2;
monster.vy = 0;
}
}
//Move the character up or down if it's
//already closer on the x axis
else
{
if(_character.y < monster.y)
{
//Up
monster.vx = 0;
monster.vy = -2;
}
else
{
//Down
monster.vx = 0;
monster.vy = 2;
}
}
This modification lets the monsters zero in on the character with surgical precision. It would make for a very difficult game, but by experimenting with a bit of extra added randomness you'll be able find a good balance that will make for a fun and challenging game. You'll find all this working code in the comments ofchangeMonsterDirection
method in theIntelligentMonsters
class.
This has been your first introduction to area of game design called Artificial Intelligence (usually referred to as AI). It's a big topic, and if you're serious about game design (which you are!) you'll want to explore it in much more depth.
A further modification that you'll want to make to Monster Mayhem is to add obstacles, like a maze for the monsters to navigate around. You could do this by using theCollision.block
method that you used in the previous two chapters and make the monsters change their directions when they hit a section of the wall. But because each monster will be checking for collisions between each and every section of the wall,
you'd have to manage a huge amount of repetitive code. In
Chapter 10
you'll learn how to handle multiple object collisions and I'll revisit this problem with a slice of very efficient and compact code.
You may also want to make your monsters find their way intelligently around a maze, possibly by the shortest path. To do this, you'll need to look at the
tile-based
approach to building games and implement a famous piece of code called
A-Star.
You can find out how to do all of this in
Advanced Game Design with Flash.
The structure for building games that you've used in this chapter will likely become the model for all the games you build from now on. Here's a quick view of how it works: