/src/org/flixel/flixOld/org/flixel/FlxGame.as
ActionScript | 801 lines | 529 code | 56 blank | 216 comment | 125 complexity | 6e704ba2c4f7600af95539d26b48923c MD5 | raw file
- package org.flixel
- {
- import flash.display.Bitmap;
- import flash.display.BitmapData;
- import flash.display.Graphics;
- import flash.display.Sprite;
- import flash.display.StageAlign;
- import flash.display.StageScaleMode;
- import flash.events.*;
- import flash.geom.Point;
- import flash.text.AntiAliasType;
- import flash.text.GridFitType;
- import flash.text.TextField;
- import flash.text.TextFormat;
- import flash.ui.Mouse;
- import flash.utils.Timer;
- import flash.utils.getTimer;
-
- import org.flixel.plugin.TimerManager;
- import org.flixel.system.FlxDebugger;
- import org.flixel.system.FlxReplay;
- /**
- * FlxGame is the heart of all flixel games, and contains a bunch of basic game loops and things.
- * It is a long and sloppy file that you shouldn't have to worry about too much!
- * It is basically only used to create your game object in the first place,
- * after that FlxG and FlxState have all the useful stuff you actually need.
- *
- * @author Adam Atomic
- */
- public class FlxGame extends Sprite
- {
- [Embed(source="data/nokiafc22.ttf",fontFamily="system",embedAsCFF="false")] protected var junk:String;
- [Embed(source="data/beep.mp3")] protected var SndBeep:Class;
- [Embed(source="data/logo.png")] protected var ImgLogo:Class;
- /**
- * Sets 0, -, and + to control the global volume sound volume.
- * @default true
- */
- public var useSoundHotKeys:Boolean;
- /**
- * Tells flixel to use the default system mouse cursor instead of custom Flixel mouse cursors.
- * @default false
- */
- public var useSystemCursor:Boolean;
- /**
- * Initialize and allow the flixel debugger overlay even in release mode.
- * Also useful if you don't use FlxPreloader!
- * @default false
- */
- public var forceDebugger:Boolean;
- /**
- * Current game state.
- */
- internal var _state:FlxState;
- /**
- * Mouse cursor.
- */
- internal var _mouse:Sprite;
-
- /**
- * Class type of the initial/first game state for the game, usually MenuState or something like that.
- */
- protected var _iState:Class;
- /**
- * Whether the game object's basic initialization has finished yet.
- */
- protected var _created:Boolean;
-
- /**
- * Total number of milliseconds elapsed since game start.
- */
- protected var _total:uint;
- /**
- * Total number of milliseconds elapsed since last update loop.
- * Counts down as we step through the game loop.
- */
- protected var _accumulator:int;
- /**
- * Whether the Flash player lost focus.
- */
- protected var _lostFocus:Boolean;
- /**
- * Milliseconds of time per step of the game loop. FlashEvent.g. 60 fps = 16ms.
- */
- internal var _step:uint;
- /**
- * Framerate of the Flash player (NOT the game loop). Default = 30.
- */
- internal var _flashFramerate:uint;
- /**
- * Max allowable accumulation (see _accumulator).
- * Should always (and automatically) be set to roughly 2x the flash player framerate.
- */
- internal var _maxAccumulation:uint;
- /**
- * If a state change was requested, the new state object is stored here until we switch to it.
- */
- internal var _requestedState:FlxState;
- /**
- * A flag for keeping track of whether a game reset was requested or not.
- */
- internal var _requestedReset:Boolean;
- /**
- * The "focus lost" screen (see <code>createFocusScreen()</code>).
- */
- protected var _focus:Sprite;
- /**
- * The sound tray display container (see <code>createSoundTray()</code>).
- */
- protected var _soundTray:Sprite;
- /**
- * Helps us auto-hide the sound tray after a volume change.
- */
- protected var _soundTrayTimer:Number;
- /**
- * Helps display the volume bars on the sound tray.
- */
- protected var _soundTrayBars:Array;
- /**
- * The debugger overlay object.
- */
- internal var _debugger:FlxDebugger;
- /**
- * A handy boolean that keeps track of whether the debugger exists and is currently visible.
- */
- internal var _debuggerUp:Boolean;
-
- /**
- * Container for a game replay object.
- */
- internal var _replay:FlxReplay;
- /**
- * Flag for whether a playback of a recording was requested.
- */
- internal var _replayRequested:Boolean;
- /**
- * Flag for whether a new recording was requested.
- */
- internal var _recordingRequested:Boolean;
- /**
- * Flag for whether a replay is currently playing.
- */
- internal var _replaying:Boolean;
- /**
- * Flag for whether a new recording is being made.
- */
- internal var _recording:Boolean;
- /**
- * Array that keeps track of keypresses that can cancel a replay.
- * Handy for skipping cutscenes or getting out of attract modes!
- */
- internal var _replayCancelKeys:Array;
- /**
- * Helps time out a replay if necessary.
- */
- internal var _replayTimer:int;
- /**
- * This function, if set, is triggered when the callback stops playing.
- */
- internal var _replayCallback:Function;
- /**
- * Instantiate a new game object.
- *
- * @param GameSizeX The width of your game in game pixels, not necessarily final display pixels (see Zoom).
- * @param GameSizeY The height of your game in game pixels, not necessarily final display pixels (see Zoom).
- * @param InitialState The class name of the state you want to create and switch to first (e.g. MenuState).
- * @param Zoom The default level of zoom for the game's cameras (e.g. 2 = all pixels are now drawn at 2x). Default = 1.
- * @param GameFramerate How frequently the game should update (default is 60 times per second).
- * @param FlashFramerate Sets the actual display framerate for Flash player (default is 30 times per second).
- * @param UseSystemCursor Whether to use the default OS mouse pointer, or to use custom flixel ones.
- */
- public function FlxGame(GameSizeX:uint,GameSizeY:uint,InitialState:Class,Zoom:Number=1,GameFramerate:uint=60,FlashFramerate:uint=30,UseSystemCursor:Boolean=false)
- {
- //super high priority init stuff (focus, mouse, etc)
- _lostFocus = false;
- _focus = new Sprite();
- _focus.visible = false;
- _soundTray = new Sprite();
- _mouse = new Sprite()
-
- //basic display and update setup stuff
- FlxG.init(this,GameSizeX,GameSizeY,Zoom);
- FlxG.framerate = GameFramerate;
- FlxG.flashFramerate = FlashFramerate;
- _accumulator = _step;
- _total = 0;
- _state = null;
- useSoundHotKeys = true;
- useSystemCursor = UseSystemCursor;
- if(!useSystemCursor)
- flash.ui.Mouse.hide();
- forceDebugger = false;
- _debuggerUp = false;
-
- //replay data
- _replay = new FlxReplay();
- _replayRequested = false;
- _recordingRequested = false;
- _replaying = false;
- _recording = false;
-
- //then get ready to create the game object for real
- _iState = InitialState;
- _requestedState = null;
- _requestedReset = true;
- _created = false;
- addEventListener(Event.ENTER_FRAME, create);
- }
-
- /**
- * Makes the little volume tray slide out.
- *
- * @param Silent Whether or not it should beep.
- */
- internal function showSoundTray(Silent:Boolean=false):void
- {
- if(!Silent)
- FlxG.play(SndBeep);
- _soundTrayTimer = 1;
- _soundTray.y = 0;
- _soundTray.visible = true;
- var globalVolume:uint = Math.round(FlxG.volume*10);
- if(FlxG.mute)
- globalVolume = 0;
- for (var i:uint = 0; i < _soundTrayBars.length; i++)
- {
- if(i < globalVolume) _soundTrayBars[i].alpha = 1;
- else _soundTrayBars[i].alpha = 0.5;
- }
- }
- /**
- * Internal event handler for input and focus.
- *
- * @param FlashEvent Flash keyboard event.
- */
- protected function onKeyUp(FlashEvent:KeyboardEvent):void
- {
- if(_debuggerUp && _debugger.watch.editing)
- return;
- if(!FlxG.mobile)
- {
- if((_debugger != null) && ((FlashEvent.keyCode == 192) || (FlashEvent.keyCode == 220)))
- {
- _debugger.visible = !_debugger.visible;
- _debuggerUp = _debugger.visible;
- if(_debugger.visible)
- flash.ui.Mouse.show();
- else if(!useSystemCursor)
- flash.ui.Mouse.hide();
- //_console.toggle();
- return;
- }
- if(useSoundHotKeys)
- {
- var c:int = FlashEvent.keyCode;
- var code:String = String.fromCharCode(FlashEvent.charCode);
- switch(c)
- {
- case 48:
- case 96:
- FlxG.mute = !FlxG.mute;
- if(FlxG.volumeHandler != null)
- FlxG.volumeHandler(FlxG.mute?0:FlxG.volume);
- showSoundTray();
- return;
- case 109:
- case 189:
- FlxG.mute = false;
- FlxG.volume = FlxG.volume - 0.1;
- showSoundTray();
- return;
- case 107:
- case 187:
- FlxG.mute = false;
- FlxG.volume = FlxG.volume + 0.1;
- showSoundTray();
- return;
- default:
- break;
- }
- }
- }
- if(_replaying)
- return;
- FlxG.keys.handleKeyUp(FlashEvent);
- }
-
- /**
- * Internal event handler for input and focus.
- *
- * @param FlashEvent Flash keyboard event.
- */
- protected function onKeyDown(FlashEvent:KeyboardEvent):void
- {
- if(_debuggerUp && _debugger.watch.editing)
- return;
- if(_replaying && (_replayCancelKeys != null) && (_debugger == null) && (FlashEvent.keyCode != 192) && (FlashEvent.keyCode != 220))
- {
- var cancel:Boolean = false;
- var replayCancelKey:String;
- var i:uint = 0;
- var l:uint = _replayCancelKeys.length;
- while(i < l)
- {
- replayCancelKey = _replayCancelKeys[i++];
- if((replayCancelKey == "ANY") || (FlxG.keys.getKeyCode(replayCancelKey) == FlashEvent.keyCode))
- {
- if(_replayCallback != null)
- {
- _replayCallback();
- _replayCallback = null;
- }
- else
- FlxG.stopReplay();
- break;
- }
- }
- return;
- }
- FlxG.keys.handleKeyDown(FlashEvent);
- }
-
- /**
- * Internal event handler for input and focus.
- *
- * @param FlashEvent Flash mouse event.
- */
- protected function onMouseDown(FlashEvent:MouseEvent):void
- {
- if(_debuggerUp)
- {
- if(_debugger.hasMouse)
- return;
- if(_debugger.watch.editing)
- _debugger.watch.submit();
- }
- if(_replaying && (_replayCancelKeys != null))
- {
- var replayCancelKey:String;
- var i:uint = 0;
- var l:uint = _replayCancelKeys.length;
- while(i < l)
- {
- replayCancelKey = _replayCancelKeys[i++] as String;
- if((replayCancelKey == "MOUSE") || (replayCancelKey == "ANY"))
- {
- if(_replayCallback != null)
- {
- _replayCallback();
- _replayCallback = null;
- }
- else
- FlxG.stopReplay();
- break;
- }
- }
- return;
- }
- FlxG.mouse.handleMouseDown(FlashEvent);
- }
-
- /**
- * Internal event handler for input and focus.
- *
- * @param FlashEvent Flash mouse event.
- */
- protected function onMouseUp(FlashEvent:MouseEvent):void
- {
- if((_debuggerUp && _debugger.hasMouse) || _replaying)
- return;
- FlxG.mouse.handleMouseUp(FlashEvent);
- }
-
- /**
- * Internal event handler for input and focus.
- *
- * @param FlashEvent Flash mouse event.
- */
- protected function onMouseWheel(FlashEvent:MouseEvent):void
- {
- if((_debuggerUp && _debugger.hasMouse) || _replaying)
- return;
- FlxG.mouse.handleMouseWheel(FlashEvent);
- }
-
- /**
- * Internal event handler for input and focus.
- *
- * @param FlashEvent Flash event.
- */
- protected function onFocus(FlashEvent:Event=null):void
- {
- if(!_debuggerUp && !useSystemCursor)
- flash.ui.Mouse.hide();
- FlxG.resetInput();
- _lostFocus = _focus.visible = false;
- stage.frameRate = _flashFramerate;
- FlxG.resumeSounds();
- }
-
- /**
- * Internal event handler for input and focus.
- *
- * @param FlashEvent Flash event.
- */
- protected function onFocusLost(FlashEvent:Event=null):void
- {
- if((x != 0) || (y != 0))
- {
- x = 0;
- y = 0;
- }
- flash.ui.Mouse.show();
- _lostFocus = _focus.visible = true;
- stage.frameRate = 10;
- FlxG.pauseSounds();
- }
-
- /**
- * Handles the onEnterFrame call and figures out how many updates and draw calls to do.
- *
- * @param FlashEvent Flash event.
- */
- protected function onEnterFrame(FlashEvent:Event=null):void
- {
- var mark:uint = getTimer();
- var elapsedMS:uint = mark-_total;
- _total = mark;
- updateSoundTray(elapsedMS);
- if(!_lostFocus)
- {
- if((_debugger != null) && _debugger.vcr.paused)
- {
- if(_debugger.vcr.stepRequested)
- {
- _debugger.vcr.stepRequested = false;
- step();
- }
- }
- else
- {
- _accumulator += elapsedMS;
- if(_accumulator > _maxAccumulation)
- _accumulator = _maxAccumulation;
- while(_accumulator >= _step)
- {
- step();
- _accumulator = _accumulator - _step;
- }
- }
-
- FlxBasic._VISIBLECOUNT = 0;
- draw();
-
- if(_debuggerUp)
- {
- _debugger.perf.flash(elapsedMS);
- _debugger.perf.visibleObjects(FlxBasic._VISIBLECOUNT);
- _debugger.perf.update();
- _debugger.watch.update();
- }
- }
- }
- /**
- * If there is a state change requested during the update loop,
- * this function handles actual destroying the old state and related processes,
- * and calls creates on the new state and plugs it into the game object.
- */
- protected function switchState():void
- {
- //Basic reset stuff
- FlxG.resetCameras();
- FlxG.resetInput();
- FlxG.destroySounds();
- FlxG.clearBitmapCache();
-
- //Clear the debugger overlay's Watch window
- if(_debugger != null)
- _debugger.watch.removeAll();
-
- //Clear any timers left in the timer manager
- var timerManager:TimerManager = FlxTimer.manager;
- if(timerManager != null)
- timerManager.clear();
-
- //Destroy the old state (if there is an old state)
- if(_state != null)
- _state.destroy();
-
- //Finally assign and create the new state
- _state = _requestedState;
- _state.create();
- }
-
- /**
- * This is the main game update logic section.
- * The onEnterFrame() handler is in charge of calling this
- * the appropriate number of times each frame.
- * This block handles state changes, replays, all that good stuff.
- */
- protected function step():void
- {
- //handle game reset request
- if(_requestedReset)
- {
- _requestedReset = false;
- _requestedState = new _iState();
- _replayTimer = 0;
- _replayCancelKeys = null;
- FlxG.reset();
- }
-
- //handle replay-related requests
- if(_recordingRequested)
- {
- _recordingRequested = false;
- _replay.create(FlxG.globalSeed);
- _recording = true;
- if(_debugger != null)
- {
- _debugger.vcr.recording();
- FlxG.log("FLIXEL: starting new flixel gameplay record.");
- }
- }
- else if(_replayRequested)
- {
- _replayRequested = false;
- _replay.rewind();
- FlxG.globalSeed = _replay.seed;
- if(_debugger != null)
- _debugger.vcr.playing();
- _replaying = true;
- }
-
- //handle state switching requests
- if(_state != _requestedState)
- switchState();
-
- //finally actually step through the game physics
- FlxBasic._ACTIVECOUNT = 0;
- if(_replaying)
- {
- _replay.playNextFrame();
- if(_replayTimer > 0)
- {
- _replayTimer -= _step;
- if(_replayTimer <= 0)
- {
- if(_replayCallback != null)
- {
- _replayCallback();
- _replayCallback = null;
- }
- else
- FlxG.stopReplay();
- }
- }
- if(_replaying && _replay.finished)
- {
- FlxG.stopReplay();
- if(_replayCallback != null)
- {
- _replayCallback();
- _replayCallback = null;
- }
- }
- if(_debugger != null)
- _debugger.vcr.updateRuntime(_step);
- }
- else
- FlxG.updateInput();
- if(_recording)
- {
- _replay.recordFrame();
- if(_debugger != null)
- _debugger.vcr.updateRuntime(_step);
- }
- update();
- FlxG.mouse.wheel = 0;
- if(_debuggerUp)
- _debugger.perf.activeObjects(FlxBasic._ACTIVECOUNT);
- }
- /**
- * This function just updates the soundtray object.
- */
- protected function updateSoundTray(MS:Number):void
- {
- //animate stupid sound tray thing
-
- if(_soundTray != null)
- {
- if(_soundTrayTimer > 0)
- _soundTrayTimer -= MS/1000;
- else if(_soundTray.y > -_soundTray.height)
- {
- _soundTray.y -= (MS/1000)*FlxG.height*2;
- if(_soundTray.y <= -_soundTray.height)
- {
- _soundTray.visible = false;
-
- //Save sound preferences
- var soundPrefs:FlxSave = new FlxSave();
- if(soundPrefs.bind("flixel"))
- {
- if(soundPrefs.data.sound == null)
- soundPrefs.data.sound = new Object;
- soundPrefs.data.sound.mute = FlxG.mute;
- soundPrefs.data.sound.volume = FlxG.volume;
- soundPrefs.close();
- }
- }
- }
- }
- }
-
- /**
- * This function is called by step() and updates the actual game state.
- * May be called multiple times per "frame" or draw call.
- */
- protected function update():void
- {
- var mark:uint = getTimer();
-
- FlxG.elapsed = FlxG.timeScale*(_step/1000);
- FlxG.updateSounds();
- FlxG.updatePlugins();
- _state.update();
- FlxG.updateCameras();
-
- if(_debuggerUp)
- _debugger.perf.flixelUpdate(getTimer()-mark);
- }
-
- /**
- * Goes through the game state and draws all the game objects and special effects.
- */
- protected function draw():void
- {
- var mark:uint = getTimer();
- FlxG.lockCameras();
- _state.draw();
- FlxG.drawPlugins();
- FlxG.unlockCameras();
- if(_debuggerUp)
- _debugger.perf.flixelDraw(getTimer()-mark);
- }
-
- /**
- * Used to instantiate the guts of the flixel game object once we have a valid reference to the root.
- *
- * @param FlashEvent Just a Flash system event, not too important for our purposes.
- */
- protected function create(FlashEvent:Event):void
- {
- if(root == null)
- return;
- removeEventListener(Event.ENTER_FRAME, create);
- _total = getTimer();
-
- //Set up the view window and double buffering
- stage.scaleMode = StageScaleMode.NO_SCALE;
- stage.align = StageAlign.TOP_LEFT;
- stage.frameRate = _flashFramerate;
-
- //Add basic input event listeners and mouse container
- stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
- stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
- stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
- stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
- stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
- addChild(_mouse);
-
- //Let mobile devs opt out of unnecessary overlays.
- if(!FlxG.mobile)
- {
- //Debugger overlay
- if(FlxG.debug || forceDebugger)
- {
- _debugger = new FlxDebugger(FlxG.width*FlxCamera.defaultZoom,FlxG.height*FlxCamera.defaultZoom);
- addChild(_debugger);
- }
-
- //Volume display tab
- createSoundTray();
-
- //Focus gained/lost monitoring
- stage.addEventListener(Event.DEACTIVATE, onFocusLost);
- stage.addEventListener(Event.ACTIVATE, onFocus);
- createFocusScreen();
- }
-
- //Finally, set up an event for the actual game loop stuff.
- addEventListener(Event.ENTER_FRAME, onEnterFrame);
- }
-
- /**
- * Sets up the "sound tray", the little volume meter that pops down sometimes.
- */
- protected function createSoundTray():void
- {
- _soundTray.visible = false;
- _soundTray.scaleX = 2;
- _soundTray.scaleY = 2;
- var tmp:Bitmap = new Bitmap(new BitmapData(80,30,true,0x7F000000));
- _soundTray.x = (FlxG.width/2)*FlxCamera.defaultZoom-(tmp.width/2)*_soundTray.scaleX;
- _soundTray.addChild(tmp);
-
- var text:TextField = new TextField();
- text.width = tmp.width;
- text.height = tmp.height;
- text.multiline = true;
- text.wordWrap = true;
- text.selectable = false;
- text.embedFonts = true;
- text.antiAliasType = AntiAliasType.NORMAL;
- text.gridFitType = GridFitType.PIXEL;
- text.defaultTextFormat = new TextFormat("system",8,0xffffff,null,null,null,null,null,"center");;
- _soundTray.addChild(text);
- text.text = "VOLUME";
- text.y = 16;
-
- var bx:uint = 10;
- var by:uint = 14;
- _soundTrayBars = new Array();
- var i:uint = 0;
- while(i < 10)
- {
- tmp = new Bitmap(new BitmapData(4,++i,false,0xffffff));
- tmp.x = bx;
- tmp.y = by;
- _soundTrayBars.push(_soundTray.addChild(tmp));
- bx += 6;
- by--;
- }
-
- _soundTray.y = -_soundTray.height;
- _soundTray.visible = false;
- addChild(_soundTray);
-
- //load saved sound preferences for this game if they exist
- var soundPrefs:FlxSave = new FlxSave();
- if(soundPrefs.bind("flixel") && (soundPrefs.data.sound != null))
- {
- if(soundPrefs.data.sound.volume != null)
- FlxG.volume = soundPrefs.data.sound.volume;
- if(soundPrefs.data.sound.mute != null)
- FlxG.mute = soundPrefs.data.sound.mute;
- soundPrefs.destroy();
- }
- }
-
- /**
- * Sets up the darkened overlay with the big white "play" button that appears when a flixel game loses focus.
- */
- protected function createFocusScreen():void
- {
- var gfx:Graphics = _focus.graphics;
- var screenWidth:uint = FlxG.width*FlxCamera.defaultZoom;
- var screenHeight:uint = FlxG.height*FlxCamera.defaultZoom;
-
- //draw transparent black backdrop
- gfx.moveTo(0,0);
- gfx.beginFill(0,0.5);
- gfx.lineTo(screenWidth,0);
- gfx.lineTo(screenWidth,screenHeight);
- gfx.lineTo(0,screenHeight);
- gfx.lineTo(0,0);
- gfx.endFill();
-
- //draw white arrow
- var halfWidth:uint = screenWidth/2;
- var halfHeight:uint = screenHeight/2;
- var helper:uint = FlxU.min(halfWidth,halfHeight)/3;
- gfx.moveTo(halfWidth-helper,halfHeight-helper);
- gfx.beginFill(0xffffff,0.65);
- gfx.lineTo(halfWidth+helper,halfHeight);
- gfx.lineTo(halfWidth-helper,halfHeight+helper);
- gfx.lineTo(halfWidth-helper,halfHeight-helper);
- gfx.endFill();
-
- var logo:Bitmap = new ImgLogo();
- logo.scaleX = int(helper/10);
- if(logo.scaleX < 1)
- logo.scaleX = 1;
- logo.scaleY = logo.scaleX;
- logo.x -= logo.scaleX;
- logo.alpha = 0.35;
- _focus.addChild(logo);
- addChild(_focus);
- }
- }
- }