The majority of Flash games tend to be quite simple in nature. They are 5 – 10 minute distractions that are played during lunch, or when the boss isn’t looking. This simplicity is also reflected in the typical control scheme employed by Flash games: mouse input with a single left click. It’s intuitive (someone who will only devote a few minutes of their time to playing the game isn’t going to want to read a help page with a complicated control scheme), and Flash doesn’t let you (easily) work with the right mouse button anyway.
Thankfully our top down shooter style of game play lends itself nicely to this simple control scheme. The player will simply move the mouse around the screen to move the players ship and click the left mouse button to fire weapons. But before we can create a game object that represents the players ship we first need a way of detecting where the mouse has been moved to, and when the mouse button has been clicked. Lets look at what changes have to be made to the main.mxml file to accommodate this.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
width="600"
height="400"
frameRate="100"
creationComplete="creationComplete()"
enterFrame="enterFrame(event)"
click="click(event)"
mouseDown="mouseDown(event)"
mouseUp="mouseUp(event)"
mouseMove="mouseMove(event)"
currentState="MainMenu">
<mx:states>
<mx:State
name="Game"
enterState="enterGame(event)"
exitState="exitGame(event)">
</mx:State>
<mx:State name="MainMenu">
<mx:AddChild relativeTo="{myCanvas}" position="lastChild">
<mx:Button x="525" y="368" label="Start" id="btnStart" click="startGameClicked(event)"/>
</mx:AddChild>
</mx:State>
</mx:states>
<mx:Canvas x="0" y="0" width="100%" height="100%" id="myCanvas"/>
<mx:Script>
<![CDATA[
protected var inGame:Boolean = false;
public function creationComplete():void
{
}
public function enterFrame(event:Event):void
{
if (inGame)
{
GameObjectManager.Instance.enterFrame();
myCanvas.graphics.clear();
myCanvas.graphics.beginBitmapFill(GameObjectManager.Instance.backBuffer, null, false, false);
myCanvas.graphics.drawRect(0, 0, this.width, this.height);
myCanvas.graphics.endFill();
}
}
private function click(event:MouseEvent):void
{
GameObjectManager.Instance.click(event);
}
private function mouseDown(event:MouseEvent):void
{
GameObjectManager.Instance.mouseDown(event);
}
private function mouseUp(event:MouseEvent):void
{
GameObjectManager.Instance.mouseUp(event);
}
private function mouseMove(event:MouseEvent):void
{
GameObjectManager.Instance.mouseMove(event);
}
protected function startGameClicked(event:Event):void
{
currentState = "Game"
}
protected function enterGame(event:Event):void
{
Mouse.hide();
GameObjectManager.Instance.startup();
Level.Instance.startup();
inGame = true;
}
protected function exitGame(event:Event):void
{
Mouse.show();
Level.Instance.shutdown();
GameObjectManager.Instance.shutdown();
inGame = false;
}
]]>
</mx:Script>
</mx:Application>
We have added functions to listen to 4 new events: mouseMove, click, mouseDown and mouseUp. MouseMove is, as you would expect, called when the mouse has been moved. It allows us to monitor(监听) the position of the mouse cursor over the flash player window. Similarly click monitors when the mouse button has been clicked (i.e. pressed and released). When the mouse button is pressed mouseDown is fired, and when it is released mouseUp is fired. We need the ability to monitor the mouseDown and mouseUp events individually (as opposed to the click event which fired after the mouse has been pressed and then released) because eventually we want the player to fire weapons when the mouse button is pressed and stop when it is released.
The four new functions have the same name as the event as their respective event, and simply pass the message along to the GameObjectManager.
package
{
import flash.display.*;
import flash.events.*;
import mx.collections.*;
import mx.core.*;
public class GameObjectManager
{
// double buffer
public var backBuffer:BitmapData;
// colour to use to clear backbuffer with
public var clearColor:uint = 0xFF0043AB;
// static instance
protected static var instance:GameObjectManager = null;
// the last frame time
protected var lastFrame:Date;
// a collection of the GameObjects
protected var gameObjects:ArrayCollection = new ArrayCollection();
// a collection where new GameObjects are placed, to avoid adding items
// to gameObjects while in the gameObjects collection while it is in a loop
protected var newGameObjects:ArrayCollection = new ArrayCollection();
// a collection where removed GameObjects are placed, to avoid removing items
// to gameObjects while in the gameObjects collection while it is in a loop
protected var removedGameObjects:ArrayCollection = new ArrayCollection();
static public function get Instance():GameObjectManager
{
if ( instance == null )
instance = new GameObjectManager();
return instance;
}
public function GameObjectManager()
{
if ( instance != null )
throw new Error( "Only one Singleton instance should be instantiated" );
backBuffer = new BitmapData(Application.application.width, Application.application.height, false);
}
public function startup():void
{
lastFrame = new Date();
}
public function shutdown():void
{
shutdownAll();
}
public function enterFrame():void
{
// Calculate the time since the last frame
var thisFrame:Date = new Date();
var seconds:Number = (thisFrame.getTime() - lastFrame.getTime())/1000.0;
lastFrame = thisFrame;
removeDeletedGameObjects();
insertNewGameObjects();
Level.Instance.enterFrame(seconds);
// now allow objects to update themselves
for each (var gameObject:GameObject in gameObjects)
{
if (gameObject.inuse)
gameObject.enterFrame(seconds);
}
drawObjects();
}
public function click(event:MouseEvent):void
{
for each (var gameObject:GameObject in gameObjects)
{
if (gameObject.inuse) gameObject.click(event);
}
}
public function mouseDown(event:MouseEvent):void
{
for each (var gameObject:GameObject in gameObjects)
{
if (gameObject.inuse) gameObject.mouseDown(event);
}
}
public function mouseUp(event:MouseEvent):void
{
for each (var gameObject:GameObject in gameObjects)
{
if (gameObject.inuse) gameObject.mouseUp(event);
}
}
public function mouseMove(event:MouseEvent):void
{
for each (var gameObject:GameObject in gameObjects)
{
if (gameObject.inuse) gameObject.mouseMove(event);
}
}
protected function drawObjects():void
{
backBuffer.fillRect(backBuffer.rect, clearColor);
// draw the objects
for each (var gameObject:GameObject in gameObjects)
{
if (gameObject.inuse)
gameObject.copyToBackBuffer(backBuffer);
}
}
public function addGameObject(gameObject:GameObject):void
{
newGameObjects.addItem(gameObject);
}
public function removeGameObject(gameObject:GameObject):void
{
removedGameObjects.addItem(gameObject);
}
protected function shutdownAll():void
{
// don't dispose objects twice
for each (var gameObject:GameObject in gameObjects)
{
var found:Boolean = false;
for each (var removedObject:GameObject in removedGameObjects)
{
if (removedObject == gameObject)
{
found = true;
break;
}
}
if (!found)
gameObject.shutdown();
}
}
protected function insertNewGameObjects():void
{
for each (var gameObject:GameObject in newGameObjects)
{
for (var i:int = 0; i < gameObjects.length; ++i)
{
if (gameObjects.getItemAt(i).zOrder > gameObject.zOrder ||
gameObjects.getItemAt(i).zOrder == -1)
break;
}
gameObjects.addItemAt(gameObject, i);
}
newGameObjects.removeAll();
}
protected function removeDeletedGameObjects():void
{
// insert the object acording to it's z position
for each (var removedObject:GameObject in removedGameObjects)
{
var i:int = 0;
for (i = 0; i < gameObjects.length; ++i)
{
if (gameObjects.getItemAt(i) == removedObject)
{
gameObjects.removeItemAt(i);
break;
}
}
}
removedGameObjects.removeAll();
}
}
}