Page 1 of 1

Making flash games for the non-flash developer Part II Continues to discusses how to start developing games using Flex 3 and

#1 coden4fun  Icon User is offline

  • if(coden4fun.NotTired){ ContinueToCode(); }
  • member icon

Reputation: 27
  • View blog
  • Posts: 696
  • Joined: 27-February 09

Posted 12 March 2009 - 04:23 AM

Preface and a Follow up
This is part 2 of an 8 part series of writing a finished flash game using Flex and ActionScript 3. Here I will discuss precisely what was mentioned in step 2 for our Current Goals in completing this project, and in great detail discuss how I go there. Upon completing this tutorial you will have learned how to customize a Flex button using the degrafa library, customize it, so we can quickly edit the button using a CSS template, structure our file system, so our project can be organized through out the duration of this project, create all states of the game, create the menu, and make the buttons play sound as they're moused over, clicked on, and finally go to the specific screen the buttons calls for when triggered. That's a lot, so let's quickly recap what we've done, and continue. Taking a look at our goal again we see the following.

Obtaining the Degrafa lib

First we're going to need a neat little library that's going to help us out, so we don't have to spend much time coding. This library can be found at the Degrafa's download page located here. Next we need to add this library to our project, so we can utilize the library. This is actually a very simple process. Simply go to the place where unzipped the downloaded folder, right click > copy, and then make sure you have highlighted your lib folder in the Flex Navigator right click > paste. Now the workspace will do the rest, and you're ready to use the degrafa library.

Setting up our Navigator and File System

Having a neat well organized file system when working on any project is very crcucial, so we're going to make sure that we have a neat file system that makes sense, is easy to navigate so we don't spend time reconstructing this later down the road. Below is an image of how I have made my file system, for this current project. If you mimic my file system, and follow the tutorials carefully then you shall get no errors, and optimize your time on actually programming the game part of this project.

Posted Image

As you can see from above my file system I have a media folder outside of the source folder and within that media folder I have a sub folder called sfx (for sound effects) that only contain the sound effects. Inside the source folder are only source files, for only source files should go into the source folder. Then inside the source folder I have organized it even more by creating a sub folder called component, which is if you can guess it is a custom flex button component, which we I shall explain right now.

The Custom Button

Inside the component folder lies 2 files. A standard CSS template, and an mxml file. What is occuring is we're customizing the button, and making it a skin, so we can call the css template as an mx style inside our main app where our buttons will be, and all buttons will take on this skin. Making the custom component this way also does accomplishes 2 important task that I'm all about when I program, and the are the following;

1.) Separates our code "objects" from each other
2.) Using the style sheet makes the component customizable, so if we don't like it in the future we can quickly change it.

To start this create the directory as you see above, and in the components folder create a new Flex mx component. By right clicking on the component folder and then right clicking on that folder then going to File > New MXML Component. I named it fusion, for simplicity, but name it what you desire.

Now for the code!

fusion.mxml (custom button skin)
<?xml version="1.0" encoding="utf-8"?>
<GraphicBorderSkin xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://www.degrafa.com/2007">
	
	<mx:Script>
		<![CDATA[
		
			import mx.utils.ColorUtil;
			
			[Bindable] private var awidth:Number;
			[Bindable] private var aheight:Number;
			
			//Styles
			[Bindable] private var _mainColor:uint = 0x333333;
			[Bindable] private var _downColor:uint = 0xFF0000;
			[Bindable] private var _cornerRadius:Number = 100;
			
			//updates list
			override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
			{
				super.updateDisplayList(unscaledWidth,unscaledHeight);
				awidth = unscaledWidth;
				aheight = unscaledHeight;
			}
			
		]]>
	</mx:Script>
	
	<!-- Draws the graphics of custom of our custom skin for up, over, and down states of the mx:Button -->
	
	<strokes>
		<SolidStroke id="mainStroke" color="#333333" weight="1"/>
	</strokes>
	
	<fills>
		<LinearGradientFill id="upFill" angle="90">
			<GradientStop ratio="1" color="{ColorUtil.adjustBrightness(_mainColor, 70)}" />
		</LinearGradientFill>
		<LinearGradientFill id="overFill" angle="90">
			<GradientStop ratio="0" color="{ColorUtil.adjustBrightness(_downColor,90)}"/>
			<GradientStop ratio="1" color="{_downColor}"/>
		</LinearGradientFill>
		<LinearGradientFill id="downFill" angle="90">
			<GradientStop ratio="0" color="{ColorUtil.adjustBrightness(_downColor,90)}"/>
			<GradientStop ratio="1" color="{_downColor}"/>
		</LinearGradientFill>
	</fills>
	
	<geometry>
		<GeometryComposition state="upSkin">
			<RoundedRectangle
				 cornerRadius="{_cornerRadius}"
				 width="{awidth*awidth/2 - awidth}"
				 height="{aheight}"
				 fill="{upFill}"
				 stroke="{mainStroke}"
				 />
		</GeometryComposition>
		<GeometryComposition state="overSkin">
			<RoundedRectangle
				 cornerRadius="{_cornerRadius}"
				 width="{awidth*awidth}"
				 height="{aheight}"
				 fill="{overFill}"
				 stroke="{mainStroke}"
				 />
		</GeometryComposition>
		<GeometryComposition state="downSkin">
			<RoundedRectangle
				 cornerRadius="{_cornerRadius}"
				 width="{awidth*awidth}"
				 height="{aheight}"
				 fill="{downFill}"
				 stroke="{mainStroke}"
				 />
		</GeometryComposition>
	</geometry>
	
</GraphicBorderSkin>



Let me explain this one before looking at the style sheet. And much like the previous tutorial where we discuss the mxml before the actionscript let's do the same thing here. Being persistent is key when becoming a good programmer.
<GraphicBorderSkin xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://www.degrafa.com/2007">


This is an extension of Border, which means it's an easier way of customizing the border of an mx control being able to use other degrafa classes and functions to ease this process.

<!-- Draws the graphics of custom of our custom skin for up, over, and down states of the mx:Button -->
	
	<strokes>
		<SolidStroke id="mainStroke" color="#333333" weight="1"/>
	</strokes>
	
	<fills>
		<LinearGradientFill id="upFill" angle="90">
			<GradientStop ratio="1" color="{ColorUtil.adjustBrightness(_mainColor, 70)}" />
		</LinearGradientFill>
		<LinearGradientFill id="overFill" angle="90">
			<GradientStop ratio="0" color="{ColorUtil.adjustBrightness(_downColor,90)}"/>
			<GradientStop ratio="1" color="{_downColor}"/>
		</LinearGradientFill>
		<LinearGradientFill id="downFill" angle="90">
			<GradientStop ratio="0" color="{ColorUtil.adjustBrightness(_downColor,90)}"/>
			<GradientStop ratio="1" color="{_downColor}"/>
		</LinearGradientFill>
	</fills>
	
	<geometry>
		<GeometryComposition state="upSkin">
			<RoundedRectangle
				 cornerRadius="{_cornerRadius}"
				 width="{awidth*awidth/2 - awidth}"
				 height="{aheight}"
				 fill="{upFill}"
				 stroke="{mainStroke}"
				 />
		</GeometryComposition>
		<GeometryComposition state="overSkin">
			<RoundedRectangle
				 cornerRadius="{_cornerRadius}"
				 width="{awidth*awidth}"
				 height="{aheight}"
				 fill="{overFill}"
				 stroke="{mainStroke}"
				 />
		</GeometryComposition>
		<GeometryComposition state="downSkin">
			<RoundedRectangle
				 cornerRadius="{_cornerRadius}"
				 width="{awidth*awidth}"
				 height="{aheight}"
				 fill="{downFill}"
				 stroke="{mainStroke}"
				 />
		</GeometryComposition>
	</geometry>



The code above creates the geometry of the button from the fill stroke of the border with
<strokes>
		<SolidStroke id="mainStroke" color="#333333" weight="1"/>
	</strokes>
	



to the rounded rectangles, and linear gradient fills of the rectangles. If this doesn't kinda just make sense then go to the degrafa website here, and look for their learning section here, and just simply walk through a couple of examples until you're familiarize yourself with the degrafa library. Remember we're only using degrafa for this one component, so we can have a customizable skin, so knowing the degrafa library is not a high importance, but it's just as a library should be. Learn and know as much as needed to get the job done! ;) Now for the actionscript of the component.

ActionScript of the Component


<mx:Script>
		<![CDATA[
		
			import mx.utils.ColorUtil;
			
			[Bindable] private var awidth:Number;
			[Bindable] private var aheight:Number;
			
			//Styles
			[Bindable] private var _mainColor:uint = 0x333333;
			[Bindable] private var _downColor:uint = 0xFF0000;
			[Bindable] private var _cornerRadius:Number = 100;
			
			//updates list
			override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
			{
				super.updateDisplayList(unscaledWidth,unscaledHeight);
				awidth = unscaledWidth;
				aheight = unscaledHeight;
			}
			
		]]>
	</mx:Script>



The script is pretty simple to understand. To make a skin you must override the function updateDisplayList, which simply updates the look and feel of that specific control by passing parameters of that width, and height of that control. Remember for future reference that updateDisplayList is needs to be over written anytime you're making a skin. Now for the variables. We simply just have the width, height of the component, the main color of this component. The fill color of the component as what we'll see when we first declare and utilize this skin, and the downcolor is the color of the component once it's in the phase of down (as in a button being pressed down triggering an event), and the corner radius of 100. Which kinda sounds ridiculous, but I had a very specific look and feel I was aiming for, and so the corner radius was important to have a value of 100. Making these variables bindable gives us the ability to be used in the style sheet.


Quick Overview
We create a GraphicBorderSkin, which is needed when making a customizable skin, and we override the function updateDisplayList, which is what's needed when pro grammatically making a skin, which in turn passes to it's super class the parameters we give it. Doing so let's us customize the entire border of all controls that mx has to offer, and because we have a stylesheet, and we made our variables bindable we can customize this skin in no time at all in the future if we so choose to do so. Now let's take a look at the stylesheet.

The Style Sheet

/* CSS file */
/*
*		Custom CSS style sheet 
*
*/
Button
{
	color: #000000;
	textSelectedColor: #FFFFFF;
	textRollOverColor: #FFFFFF;
	fontFamily: Arial;
	fontSize: 10;
	skin: ClassReference("components.fusion");
	mainColor: #FF0000;
	downColor: #FF0000;
	cornerRadius: 100;
}



As you can see we simply just declare the variables that are bindable in the custom component, and set it to a specific value, but then we have the reference of skin

skin: ClassReference("components.fusion");


which just tells the mx application once we reference the style sheet to that application that we have a custom skin, and that is where it's located.

Setting up the Project
Before we start to code the meat of our game let me stress one more time the importance of organization, and to create the media folder outside the source, and organize the media folder. Below under Source Files you can grab the entire project for this tutorial, so you'll have all files.


Our Main Flex Clode
Instead of going through all the code again, and since this is a progressing tutorial I think it will be wise to only talk about code that has been added. I will list the entire code, which has been added, but to save time I believe it's in our best interest only to show update code, so let's update our code.

First we must update our Application tag.
 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
	layout="absolute"
	applicationComplete="init()"
	frameRate = "120"
	width="100%"
	height="100%"
	enterFrame="update(event)"
	currentState="Menu">


Above the only thing we've added is the "currentState" attribute, which keeps handles, controls, and updates the different stages if we define any for our Flex application to be in.

Next we link / add our css style sheet that will skin our controls for us.
<mx:Style source="components/style.css" />



Now this will look like a lot of code, but in actuality it's just a bunch of code, but it's not complex at all.

<mx:states>
		<!--Main Menu scene state-->
		<mx:State name="Menu">
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">
				<mx:Button label="Start Game" mouseOver="mouseOver(event)" click="btnStartGame()" x="{Application.application.width / 2}" y="{Application.application.height / 4}" />
			</mx:AddChild>
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">	
				<mx:Button label="How 2 Play" mouseOver="mouseOver(event)" click="btnHow2Play()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 25}" />
			</mx:AddChild>
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">	
				<mx:Button label="Settings" mouseOver="mouseOver(event)" click="btnSettings()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 50}" />
			</mx:AddChild>
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">	
				<mx:Button label="Credits" mouseOver="mouseOver(event)" click="btnCredits()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 75}" />
			</mx:AddChild>
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">	
				<mx:Button label="High Scores" mouseOver="mouseOver(event)" click="btnHighScores()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 100}"/>
			</mx:AddChild>
		</mx:State>
		<!--Main Game "playing game" state-->
		<mx:State name="InGame" enterState="enterGame(event)" exitState="exitGame(event)">
			
		</mx:State>
		<!-- How 2 Play game page-->
		<mx:State name="HowToPlay">
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">
				<mx:Button label="Back to menu" mouseOver="mouseOver(event)" click="btnBackToMenu()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 25}" />
			</mx:AddChild>
		</mx:State>
		<!--Settings toggle fullscreen, adjust controls, etc..-->
		<mx:State name="Settings">
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">
				<mx:Button label="Toggle fullscreen" mouseOver="mouseOver(event)" click="toggleFullScreen()" x="{Application.application.width / 2}" y="{Application.application.height / 4}" />
			</mx:AddChild>
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">	
				<mx:Button label="Back to menu" mouseOver="mouseOver(event)" click="btnBackToMenu()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 25}" />
			</mx:AddChild>
		</mx:State>
		<!--Credit page.  Give credit where credit is due-->
		<mx:State name="Credits">
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">
				<mx:Button label="Back to menu" click="btnBackToMenu()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 25}" />
			</mx:AddChild>
		</mx:State>
		<!--High Score page.  Rank yourself amongst the best-->
		<mx:State name="HighScores">
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">
				<mx:Button label="Back to menu" click="btnBackToMenu()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 25}" />
			</mx:AddChild>
		</mx:State>
	</mx:states>
	<mx:Canvas x="0" y="0" width="100%" height="100%" id="gameCanvas"/>



We're simply creating a state for each major scene for the game, and each state has children, which can add components for that specific state. Think of this as creating multiple stages that Flash does so well, but only programmatically. Each state that you create has to reference something you can draw on, so I created a canvas called "gameCanvas" so all states can be drawn onto the screen. So instead of just going through all of the states, let's just dissect the menu state, since it's the one with the most on it, and see what's happening.

<!--Main Menu scene state-->
		<mx:State name="Menu">
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">
				<mx:Button label="Start Game" mouseOver="mouseOver(event)" click="btnStartGame()" x="{Application.application.width / 2}" y="{Application.application.height / 4}" />
			</mx:AddChild>
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">	
				<mx:Button label="How 2 Play" mouseOver="mouseOver(event)" click="btnHow2Play()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 25}" />
			</mx:AddChild>
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">	
				<mx:Button label="Settings" mouseOver="mouseOver(event)" click="btnSettings()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 50}" />
			</mx:AddChild>
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">	
				<mx:Button label="Credits" mouseOver="mouseOver(event)" click="btnCredits()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 75}" />
			</mx:AddChild>
			<mx:AddChild relativeTo="{gameCanvas}" position="absolute">	
				<mx:Button label="High Scores" mouseOver="mouseOver(event)" click="btnHighScores()" x="{Application.application.width / 2}" y="{Application.application.height / 4 + 100}"/>
			</mx:AddChild>
		</mx:State>



<mx:stage name="Menu"></mx:stage> - Specifies a specific stage for us to draw things onto the canvas. Much like a stage that is in Flash.

<mx:AddChild></mx:AddChild> - Gives us the ability to create components and throw them on the stage, but only one component per child can be created.

Then the button which we've already seen from the first tutorial. From that button the only changes we have made to it that shouldn't be familiar is the mouseOver="mouseOver(event)".

mouseOver="mouseOver(event)" - just like the click parameter except the function is called when the mouse is over that specific button.

We create a single state with the name "Menu", and in the state we add controls by adding a child to that state for each control we want to add into that state, and if we only add the controls, and don't specify a child for each control we get errors!!! So remember for each control you'd like to add to a stage it must lie within an AddChild tag. Each state is drawn on the screen according to what currentState is, so changing the currentState to a specific state that we have named will redraw our application, and only draw that specific state which is called.

Our ActionScript Code

private var requestMouseOver:URLRequest = new URLRequest("../media/sfx/btnClick.mp3");
			private var requestClick:URLRequest = new URLRequest("../media/sfx/click.mp3");
			private var requestGame:URLRequest = new URLRequest("../media/sfx/energy.mp3");
			private var sound:Sound;



Add the above code right after we declare the inGame boolean. This allows us to access the sound clips we have in the media/sfx folder. We create a URLRequest which is passed a string, and if that URL exist then it retrieves that data from that URL.

Next the toggleFullScreen function has been tweaked just a little.
//toggle fullscreen
			private function toggleFullScreen():void
			{
				try
				{
					switch(Application.application.stage.displayState)
					{
						case StageDisplayState.FULL_SCREEN:
							Application.application.stage.displayState = StageDisplayState.NORMAL;
							break;
						default:
							Application.application.stage.displayState = StageDisplayState.FULL_SCREEN;
							break;
					}
				}
				//if we can't go fullscreen trace the error
				catch(err:SecurityError)
				{
					trace (err.message);
				}
				
				Clicked();
			}



We've simply just added a call function called "Clicked()", which is used for all buttons that make a click sound. Calling a single function like this saves time and memory. This will work for every button who needs to call on the function Clicked to play a sound clip.

//start the game
			private function btnStartGame():void
			{
				currentState = "InGame";
				Clicked();
			}
			
			//go to How 2 Play game page
			private function btnHow2Play():void
			{
				currentState = "HowToPlay";
				Clicked();
			}
			
			//go to the settings page
			private function btnSettings():void
			{
				currentState = "Settings";
				Clicked();
			}
			
			//go to the credits page
			private function btnCredits():void
			{
				currentState = "Credits";
				Clicked();
			}
			
			//go to the high scores page
			private function btnHighScores():void
			{
				currentState = "HighScores";
				Clicked();
			}
			
			//back to menu
			private function btnBackToMenu():void
			{
				currentState = "Menu";
				Clicked();
			}
			
			//game started
			private function enterGame(event:Event):void
			{
				inGame = true;
			}
			
			//exiting the game
			private function exitGame(event:Event):void
			{
				inGame = false;
			}
			
			//play sound effect for all buttons who has had the mouse roll over them, and plays the sound effect.
			private function mouseOver(event:Event):void
			{
				sound = new Sound(requestMouseOver);
				sound.play();
			}
			
			//all buttons call this when they're clicked, and plays the sound effect.
			private function Clicked():void
			{
				sound = new Sound(requestClick);
				sound.play();
			}


The above code looks enormous, but I can assure you they all do the samething. Each function is being called from a specific button,, and then triggers the currentState, and defines it to a specific state, which it is requested to call on, so for instance the "How 2 Play" button is triggering the btnHowToPlay, and changes the currentstate to the state of how to play.

Finally we have the mouseOver, and clicked functions, which only purpose is to play a specific sound clip from the specified URL it request.

The Final Result
OK, that's it, and you should get a final result equivalent to the following image below.
Posted Image

Conclusion
We have done a lot. We made our own custom skin for our game, made our states for each screen/scene in the game, made our menus and didn't mess anything up from our previous work. Oh, and we got a reply, and the game we'll be making is Galaga! So to sum it up we did make all of our scenes (which in flex are called states, which acts just like scenes or stages in flash), and we made our menu with all buttons being pressed, and triggering the currentState to change to the specific one they define, and we even gave them mouseover sound effect, and a click sound effect. We finally got a respond from a user who wants to make Galaga, so we will. So, I guess until next time....

Happy Coding!

Source Code
Source Code

Is This A Good Question/Topic? 0
  • +

Replies To: Making flash games for the non-flash developer Part II

#2 Guest_zach*


Reputation:

Posted 16 February 2010 - 12:23 PM

im getting an error "Error: Could not resolve <GraphicBorderSkin> to a component implementation"
Was This Post Helpful? 0

#3 zrm22  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 16-February 10

Posted 21 February 2010 - 07:47 AM

Ok, i made an account now(im guest_zach)....

This issue is really frustrating me and i really need help
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1