Further refining the Intro screen

Now, armed with the knowledge of what happens and when, let's put it to some good use. The first thing we need to accomplish is that the instructions need to be hidden by default, and the user must click the button to display them. So here is the Intro class again, with a new line added to the constructor:

package {
	import flash.display.*;
	import flash.events.*;
	
	public class Intro extends MovieClip {
		
		public function Intro() {
			addEventListener(Event.ADDED_TO_STAGE, addedHandler);
			addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
			
			instructions_mc.visible = false;
		}
		private function addedHandler(event:Event):void {
			this.x = stage.stageWidth / 2;
			this.y = stage.stageHeight / 2;
		}
		private function removedHandler(event:Event):void {
			
		}
	}
}

The reason we are able to call instructions_mc by name is that we gave it that instance name. When you type instance names in the properties panel, the Flash compiler does you a favor and creates matching variable names that you can use in your code. It also automatically declares them for you (their instantiation is something you did by creating them manually at authoring time). This whole process is invisible to you, and all you need to care about is that it works. The new line of code in the constructor sets the visible property of the instructions to false so that the game instructions do not display initially. Remember, the constructor only runs when outside code says "new," so in our case, this instruction will only run once, because we don't intend to instantiate this class more than once.

The next thing to do is to make the instructions visible again when the instructions button is clicked. Therefore, we need to add an event listener for the CLICK event to this button, and also write an event handler function for it:

package {
	import flash.display.*;
	import flash.events.*;
	
	public class Intro extends MovieClip {
		
		public function Intro() {
			addEventListener(Event.ADDED_TO_STAGE, addedHandler);
			addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
			
			instructions_mc.visible = false;
			instructions_btn.addEventListener(MouseEvent.CLICK, showInstructions);
		}
		private function addedHandler(event:Event):void {
			this.x = stage.stageWidth / 2;
			this.y = stage.stageHeight / 2;
		}
		private function removedHandler(event:Event):void {
			
		}
		private function showInstructions(event:MouseEvent):void {
			instructions_mc.visible = !instructions_mc.visible;
		}
	}
}

In fact, the one line of code in the handler reverses the "visible" property of the instructions_mc using the not (!) operator. This will make the button work like a toggle, turning the instructions on and off. After making this change, save this file and test the movie. I find that pressing CTRL-S (save) and CTRL-ENTER in rapid succession makes a cool way to save and test.

The other thing we must do is to make the "Play the Game" button change the screen. So add this line to the consructor of the Intro.as class file:

playGame_btn.addEventListener(MouseEvent.CLICK, playGame);

Next, we must of course proceed to write the playGame function. Here we come to the horns of a dilemma, because the controlling code needs to be the code that changes screens, but the button is located in the screen that needs changing. So Intro screen's class needs to somehow tell the Main class to remove it and add the Learn screen. Of course, we haven't created or instantiated the Learn screen yet, but let's write the screen changing code anyway. We will write the screen changing code in a public function in Main.as, then call that public function from the Intro.as class. Here's the code in the Intro.as file:

private function playGame(event:MouseEvent):void {
	MovieClip(parent).gotoLearn();
}

Now, we must proceed to write the gotoLearn function in the Main.as file:

package {
	import flash.display.*;
	import flash.events.*;
	
	public class Main extends MovieClip {
		private var intro:Intro;
		
		public function Main() {
			intro = new Intro();
			addChild(intro);
		}
		public function gotoLearn():void {
			removeChild(intro);
			addChild(learn);
		}
	}
}

The only trouble with this code is that it isn't flexible enough. The reason is that as we come up with more screens, we would like to use this same function to change to the learn screen from any of those other screens, not just intro. Later there will be a Drag screen, and we are going to make it possible to go back and forth from the Drag screen to the Learn screen. The removeChild() function is finicky--if you ask it to remove something that is not on the display list, you get a nasty error message. If we ask to remove the intro screen, for example, but the intro screen is not the one current being displayed, we will run into this problem head first.

One solution to this is a favorite trick of mine: the introduction of a "blank" MovieClip variable that can take on the identity of any of these other screens. I will choose to call this variable "currentScreen." The idea is simple: whenever there is a new screen shown, that screen becomes the currentScreen. Then when we say removeChild(currentScreen), we will always be removing whichever screen was the most recent one displayed. Here's the main class again with these changes:

package {
	import flash.display.*;
	import flash.events.*;
	
	public class Main extends MovieClip {
		private var intro:Intro;
		private var currentScreen:MovieClip;
		
		public function Main() {
			intro = new Intro();
			addChild(intro);
			currentScreen = intro;
		}
		public function gotoLearn():void {
			removeChild(currentScreen);
			addChild(learn);
			currentScreen = learn;
		}
	}
}

If you try to test the movie at this point, you will get a compiler error, naturally, because we have referred to a learn object that doesn't exist (yet)! On the next page we will proceed to create and develop it.