<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Flex - TheTechlabs.com</title>
	<atom:link href="https://www.thetechlabs.com/category/flex/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.thetechlabs.com</link>
	<description>The Tech Labs</description>
	<lastBuildDate>Mon, 15 Feb 2016 10:28:21 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Connecting Flex Calendar With Database Using AMF-PHP</title>
		<link>https://www.thetechlabs.com/interfaces/connecting-flex-calendar-with-database-using-amf-php/</link>
					<comments>https://www.thetechlabs.com/interfaces/connecting-flex-calendar-with-database-using-amf-php/#comments</comments>
		
		<dc:creator><![CDATA[Ankur Arora]]></dc:creator>
		<pubDate>Sat, 29 May 2010 13:56:36 +0000</pubDate>
				<category><![CDATA[Flex]]></category>
		<category><![CDATA[Interfaces]]></category>
		<category><![CDATA[Tech Tutorials]]></category>
		<category><![CDATA[actionscript 3.0]]></category>
		<category><![CDATA[components]]></category>
		<category><![CDATA[mxml]]></category>
		<category><![CDATA[oop]]></category>
		<category><![CDATA[ria]]></category>
		<category><![CDATA[web-development]]></category>
		<guid isPermaLink="false">https://www.thetechlabs.com/?p=1853</guid>

					<description><![CDATA[<p>Connecting Flex Calendar with Database using AMF-PHP Part I: Create a Dynamic Event Calendar in Flex Builder 3 with Actionscript 3.0 This is Part II of Create a Dynamic Event...</p>
<p>The post <a href="https://www.thetechlabs.com/interfaces/connecting-flex-calendar-with-database-using-amf-php/">Connecting Flex Calendar With Database Using AMF-PHP</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2>Connecting Flex Calendar with Database using AMF-PHP</h2>
<p>Part I: <a href="https://www.thetechlabs.com/tutorials/interfaces/create-a-dynamic-event-calendar-in-flex-builder-3-with-actionscript-30/" target="_blank">Create a Dynamic Event Calendar in Flex Builder 3 with Actionscript 3.0</a></p>
<p><center><script type="text/javascript">// <![CDATA[
google_ad_client = "pub-7847584538700437"; /* 300x250, created 5/7/10 - The Tech Labs */ google_ad_slot = "0549771992"; google_ad_width = 300; google_ad_height = 250;
// ]]&gt;</script><br />
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">// <![CDATA[

// ]]&gt;</script></center><br />
This is <b>Part II</b> of <a href="https://www.thetechlabs.com/tutorials/interfaces/create-a-dynamic-event-calendar-in-flex-builder-3-with-actionscript-30/" target="_blank">Create a Dynamic Event Calendar in Flex Builder 3 with Actionscript 3.0</a> tutorial. In this tutorial, we will learn how to connect Event Calendar with the database so events will be stored in the database for future reference.</p>
<h3>Requirements</h3>
<p>Flex Builder</p>
<p><a href="http://www.adobe.com/products/flex/" target="_blank" rel="nofollow">Try/Buy</a></p>
<p>WAMP (PHP, MYSQL)</p>
<p><a href="http://www.wampserver.com/en/download.php" target="_blank" rel="nofollow">Download</a></p>
<p>AMF-PHP</p>
<p><a href="http://sourceforge.net/projects/amfphp/files/#files" target="_blank" rel="nofollow">Download</a></p>
<p>Sample Files</p>
<p><a href="https://www.thetechlabs.com/wp-content/uploads/2010/05/source.zip" rel="nofollow">Flex_Event_Calendar_Source.zip</a></p>
<h3>Pre-Requisites</h3>
<p>This tutorial assumes that you have a complete understanding of <strong>Part I</strong>. Apart from this you know AMF-PHP and you must have a idea to how to connect a Flex application with AMF-PHP using configuration file.</p>
<h3>Step 1: Creating Database</h3>
<p>The very first step is to create database with desired table. To accomplish this, I used a MySQL database which runs very well with PHP. I have created a new database for this application named <strong>calendar_events</strong> and within this database I have created a new table called <strong>tblcalendarevents</strong>. Here is the SQL script to create this database along with a table in it.</p>
<pre>-- Database: calendar_events
--
CREATE DATABASE calendar_events DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;

USE calendar_events;

-- --------------------------------------------------------

-- Table structure for table 'tblcalendarevents'

CREATE TABLE IF NOT EXISTS tblcalendarevents (
  str_calendar_events text
);</pre>
<p>Your database structure should look like below figure.</p>
<p><center><img decoding="async" src="https://www.thetechlabs.com/wp-content/uploads/2010/05/figure-1.1.jpg" alt="" width="530" height="" /></center><br />
Figure 1.1 Database Structure</p>
<p>Similarly, your table structure within this database should look like below figure.</p>
<p><center><img decoding="async" src="https://www.thetechlabs.com/wp-content/uploads/2010/05/figure-1.2.jpg" alt="" width="530" height="" /></center><br />
Figure 1.2 Table Structure</p>
<h3>Step 2: Creating MySQL Database and PHP Class</h3>
<p>Before starting anything in Flex and performing coding we need to make sure that we have our MySQL database in place. I have used WAMP to install PHP and MySQL in my Windows environment. Where I placed <strong>amfphp</strong> files under <strong>www</strong> folder of WAMP. Supposing you have installed WAMP in your C: then here is the directory structure for <strong>amfphp</strong> folder.</p>
<p>c:wampwwwamfphp</p>
<p>Under <strong>amfphp </strong>folder there is <strong>services </strong>folder where we need to create a PHP class which will connect with the database and perform insertion and retrieval process. I have named the PHP file <em>SaveEvents.php</em>. Here is the code within PHP file which will do data processing.</p>
<pre>&lt;?php
class SaveEvents
{ 
    function storeEvents($_strEvents)
    {
        $con = mysql_connect("YOUR-MYSQL-SERVER-ADDRESS","USERNAME","PASSWORD");
		if (!$con)
		{
			die('Could not connect: ' . mysql_error());
		}

		mysql_select_db("calendar_events", $con);
		mysql_query("delete FROM tblCalendarEvents");
		mysql_query("INSERT INTO tblCalendarEvents VALUES ('" . $_strEvents . "');");
		
		mysql_close($con);

		return "success";
    }

	function getEvents()
	{
		$con = mysql_connect("YOUR-MYSQL-SERVER-ADDRESS","USERNAME","PASSWORD");
		if (!$con)
		{
			die('Could not connect: ' . mysql_error());
		}

		mysql_select_db("calendar_events", $con);

		$result = mysql_query("SELECT * FROM tblCalendarEvents");
		$strEvents = "";
		while($row = mysql_fetch_array($result))
		{
			$strEvents = $strEvents . $row['str_calendar_events'];
		}

		mysql_close($con);

		if($strEvents == "")
		{
			return "empty";
		}
		else
		{
			return $strEvents;
		}
		 
	}
}
?&gt;</pre>
<p>In the above code we have 2 functions named storeEvents() and getEvents(). storeEvents function is used to store calendar events in the database and similarly getEvents is used to retrieve those values. Now to make it more easier I have used storing/retrieval of XML rather than a long list of multiple values.</p>
<h3>Step 3: Creating services-config.xml file</h3>
<p>Moving to Flex, the first step we need to do is to create <strong>services-config.xml</strong> file. The services-config.xml file is an Extensible Markup Language (XML) document that specifies the properties of the remote services our application will use. To create this file, right click the src folder of your Flex project, then choose New &gt; File. Name the file services-config.xml, then click Finish. You will be presented with the new empty file. The default editor is the built-in XML editor, but you need to open it in the plain text editor by right clicking the file and choosing Open with &gt; Text Editor. Next, paste the following code into the file:</p>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;services-config&gt;
    &lt;services&gt;
        &lt;service id="sabreamf-flashremoting-service"
                 class="flex.messaging.services.RemotingService"
                 messageTypes="flex.messaging.messages.RemotingMessage"&gt;
            &lt;destination id="<strong>amfphp</strong>"&gt;
                &lt;channels&gt;
                    &lt;channel ref="my-amfphp"/&gt;
                &lt;/channels&gt;
                &lt;properties&gt;
                    &lt;source&gt;*&lt;/source&gt;
                &lt;/properties&gt;
            &lt;/destination&gt;
        &lt;/service&gt;
    &lt;/services&gt;

    &lt;channels&gt;
        &lt;channel-definition id="my-amfphp" class="mx.messaging.channels.AMFChannel"&gt;
            &lt;endpoint uri="<strong>http://YOUR-SERVER-ADDRESS/amfphp/gateway.php</strong>" class="flex.messaging.endpoints.AMFEndpoint"/&gt;
        &lt;/channel-definition&gt;
    &lt;/channels&gt;
&lt;/services-config&gt;</pre>
<p>The above XML block contains two key pieces of information specific to this application. The first is the id attribute of the &lt;destination&gt; node. You will use this ID to connect to the remote service in the Flex application. The other important attribute is the uri property of the &lt;endpoint&gt; node. Here, you need to place the path to your <strong>amfphp</strong> gateway file on your web server. The class attributes correspond to the method of communication: you will be using “remoting” to communicate with your services in AMF.</p>
<p>Next, you need to tell the Flex compiler about your services-config.xml file. You do so using the compiler flag -services. To add this flag, click Project &gt; Properties. A new window appears with a list on the left side. From that list, choose Flex Compiler. At the end of the existing text in the Additional compiler arguments box, add the following:</p>
<pre>-services "services-config.xml"</pre>
<p>The result should look like Figure 1.1</p>
<p><center><img decoding="async" src="https://www.thetechlabs.com/wp-content/uploads/2010/05/figure-1.1.png" alt="" /></center><br />
Figure 1.1 Adding compiler arguments</p>
<p>Click Finish, and the workspace refreshes. When complete, you will have access to your amfphp gateway from the client application.</p>
<h3>Step 4: Creating Service Connector class</h3>
<p>Next step is to create a Service Connector class which will be a singleton class and allow our application to connect with amfphp and process various data calls. To achieve this I have created a new class file under <strong>classes.remoting</strong> package. I have named this class/file as <strong>ServiceConnector.as</strong>. Let&#8217;s first have a look at the code of this class thereafter we will have description of the code.</p>
<pre>package classes.remoting
{
	import classes.events.CustomEvents;
	
	import flash.events.EventDispatcher;
	
	import mx.controls.Alert;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.remoting.RemoteObject;
	
	public class ServiceConnector extends EventDispatcher
	{
		private var m_objRemote:RemoteObject;
		private static var m_objServiceConnector:ServiceConnector;
		
		public static function getInstance():ServiceConnector
		{
			if(m_objServiceConnector == null)
				m_objServiceConnector = new ServiceConnector();
				
			return m_objServiceConnector;
		}
		
		public function connect():void
		{
			m_objRemote = new RemoteObject();
			m_objRemote.source = "SaveEvents";
			m_objRemote.destination = "amfphp";
						
			m_objRemote.storeEvents.addEventListener("result", onStoreResult);
			m_objRemote.getEvents.addEventListener("result", onFetchResult);
			m_objRemote.addEventListener("fault", onServiceFault);
			
		}
		
		public function storeEvents(_strEvents:String):void
		{
			if(_strEvents == null || _strEvents == "")
				_strEvents = "&lt;calendar /&gt;";
			
			m_objRemote.getOperation("storeEvents").send(_strEvents);
			
		}
		
		public function getEvents():void
		{
			m_objRemote.getOperation("getEvents").send();
		}
		
		private function onStoreResult(evt:ResultEvent):void
		{
			trace("Data  Stored");
		}
		
		private function onFetchResult(evt:ResultEvent):void
		{
			var obj:Object = new Object();
			obj.data = evt.result.toString();
			dispatchEvent(new CustomEvents(CustomEvents.FETCH_DB_DATA, obj));
		}
		
		private function onServiceFault(evt:FaultEvent):void
		{
			Alert.show("Cannot connect to Web service, Please check your configuration settings!", "Event Calendar");
		}

	}
}
</pre>
<p>ServiceConnector is a singleton class which allows to connect remote service i.e. amfphp through connect() method. If you see in connect() method we are calling SaveEvents as a source to Remote Object which is the class name in our PHP file. Similarly we have destination as amfphp which we defined as ID in services-config.xml file. We have also result and fault listeners with-in the connect() method which get invoked whenever a result or fault event occurred. Apart from that we have 2 public methods in this class storeEvents() and getEvents() which allows to send and retrieve data respectively to SaveEvents php class. After creating above 2 new files your directory structure should look like this.</p>
<p><center><img fetchpriority="high" decoding="async" src="https://www.thetechlabs.com/wp-content/uploads/2010/05/figure-4.1.jpg" alt="" width="289" height="482" /></center><br />
Figure 4.1 Directory Structure</p>
<h3>Step 5: Changes in main.mxml</h3>
<p>I have added couple of new methods and codes in order to achieve the functionality of connecting this application with the database. Let&#8217;s start with the main.mxml file because that is the first file which is used by the application as the main file.</p>
<p>Previously we had a method called onApplicationStart() which get executes on creation complete of the main application file. In other words this method is the very first method which gets invoked and starts the application. I have added couple of new code with in this method plus I also removed couple of lines. Let&#8217;s have a look:</p>
<div id="div_compare"><strong>Previous Version</strong></div>
<div id="div_compare_clear"></div>
<div id="div_compare">
<pre>private function onApplicationStart():void
{
    var objDate:Date = new Date();
    dtPicker.selectedDate = objDate;
    
    // create events
    monthView.addEventListener(CustomEvents.MONTH_VIEW_CLICK, onMonthViewClick);
    DataHolder.getInstance().addEventListener(CustomEvents.ADD_NEW_EVENT, onNewEventAdded);
    
    onDateChange();
}
</pre>
</div>
<div id="div_compare_margin">
<div id="div_compare_margin"><strong>Current Version</strong></div>
<pre>private function onApplicationStart():void
{
    monthView.addEventListener(CustomEvents.MONTH_VIEW_CLICK, onMonthViewClick);
    DataHolder.getInstance().addEventListener(CustomEvents.ADD_NEW_EVENT, onNewEventAdded);
    
    
    DataHolder.getInstance().addEventListener(CustomEvents.DP_CHANGED, onDataProviderChanged);
    
   
    m_objServiceConnector = ServiceConnector.getInstance();
    m_objServiceConnector.addEventListener(CustomEvents.FETCH_DB_DATA, onFetchDBData);
    
    m_objServiceConnector.connect();
    m_objServiceConnector.getEvents();
}
</pre>
</div>
<div id="div_compare_clear"></div>
<p>There is a new variable in above method which has to be declared at top level of main.mxml file. Something like this:</p>
<pre>private var m_objServiceConnector:ServiceConnector;</pre>
<p>As per current version I have added a new listener which will listen to &#8220;data provider change&#8221; event. I have also added a code to create ServiceConnector object and as well as called connect() method of ServiceConnector class. Afterwards I have invoked getEvents() which allows the application to load data from the database and behave accordingly.</p>
<p>Another method named onDateChange has been modified, let&#8217;s go through that as well.</p>
<div id="div_compare"><strong>Previous Version</strong></div>
<div id="div_compare_clear"></div>
<div id="div_compare">
<pre>private function onDateChange():void
{
    m_intCurrentDate = new Date(dtPicker.displayedYear, dtPicker.displayedMonth, dtPicker.selectedDate.date);
}
</pre>
</div>
<div id="div_compare_margin"><strong>Current Version</strong></div>
<div id="div_compare_margin">
<pre>private function onDateChange():void
{
    var currentDate:int;
    if(dtPicker.selectedDate == null)
        currentDate = new Date().date;
    else
        currentDate = dtPicker.selectedDate.date;
        
    m_intCurrentDate = new Date(dtPicker.displayedYear, dtPicker.displayedMonth, currentDate);
}
</pre>
</div>
<div id="div_compare_clear"></div>
<p>I have just added a validation of the date in this method so it won&#8217;t come as null at any point of time.</p>
<p>Next method which will get invoked is onFetchDBData which is being fired from the event listener in onApplicationStart method. Basically when we call getEvents from ServiceConnector class it dispatched FETCH_DB_DATA event which further listened by main.mxml class. Let&#8217;s have a look at onFetchDBData method:</p>
<pre>private function onFetchDBData(_event:CustomEvents):void
{							
    if(_event.object.data == "empty")
    {
        var objDate:Date = new Date();
        dtPicker.selectedDate = objDate;
        onDateChange();
    }
    else
    {
        dataProvider = _event.object.data;
    }
}
</pre>
<p>I have just added a validation of the date in this method so it won&#8217;t come as null at any point of time.</p>
<p>Next method which will get invoked is onFetchDBData which is being fired from the event listener in onApplicationStart method. Basically when we call getEvents from ServiceConnector class it dispatched FETCH_DB_DATA event which further listened by main.mxml class. Let&#8217;s have a look at onFetchDBData method.</p>
<pre>private function onFetchDBData(_event:CustomEvents):void
{							
    if(_event.object.data == "empty")
    {
        var objDate:Date = new Date();
        dtPicker.selectedDate = objDate;
        onDateChange();
    }
    else
    {
        dataProvider = _event.object.data;
    }
}
</pre>
<p>Now onFetchDBData method is the one which will either render the views with blank data otherwise if it has any data for a particular date it will list out that and render the view.</p>
<p>If you see in else condition of above method I&#8217;m storing all values(returned by our php class) in a property called <strong>dataProvider</strong>. This <strong>dataProvider</strong> property which is a getter/setter takes a XML string and converts it into a array which is further used by <strong>DataHolder</strong> class. Similarly <strong>dataProvider</strong> takes an array from DataHolder and converts it as per the requirement of the php to store it in the database. Here is the code for dataProvider property.</p>
<pre>public function set dataProvider(_strEvents:String):void
{
    var objDataXML:XML = new XML(_strEvents);
    var objArr:Array = new Array();
    for(var i:int=0; i&lt;objDataXML.event.length(); i++)
    {
        var obj:Object = new Object();
        var xml:XML = objDataXML.event[i];
        
        obj.date = new Date(Date.parse(xml.date));
        obj.hour = String(xml.hour);
        obj.meridiem = String(xml.meridiem);
        obj.mins = String(xml.mins);
        obj.desc = String(xml.desc);
        
        objArr.push(obj);
    }
    
    DataHolder.getInstance().dataProvider = objArr;
}



public function get dataProvider():String
{				
    var objCalendarData:XML = &lt;calendar /&gt;;
    for(var i:int=0; i&lt;DataHolder.getInstance().dataProvider.length; i++)
    {
        var obj:Object = DataHolder.getInstance().dataProvider[i];
        var xml:XML = &lt;event /&gt;
        xml.date = obj.date;
        xml.hour = String(obj.hour);
        xml.meridiem = String(obj.meridiem);
        xml.mins = String(obj.mins);
        xml.desc = String(obj.desc);
        
        objCalendarData.appendChild(xml);
    } 
    
    return objCalendarData.toString();
}
</pre>
<p>We have stored values retrieved from the php class into this dataProvider but we didn&#8217;t yet sent this dataProvider to the php class. To accomplish that we need to add a new line of code under onNewEventAdded method so, whenever a new event is added in the calendar it should send that value to the php class. We need to add the below line at the end of this method.</p>
<pre>m_objServiceConnector.storeEvents(dataProvider);
</pre>
<p>One last change in the main.xml file is to create a listeners method for DP_CHANGED event. This event occurs whenever there is a change in the dataProvider in DataHolder. In other words, it only executes for once, when this application load the data from the php class. Have a look at the content of this method.</p>
<pre>private function onDataProviderChanged(_event:CustomEvents):void
{
    trace("Data Updated");
    onDateChange();
}</pre>
<p>We are through with the necessary changes in main mxml file. Now let&#8217;s jump on to the next step where we will modify couple of other files.</p>
<h3>Step 6: Changes in CommonUtils and CustomEvents</h3>
<p>There are very small changes in both of these classes. I have added a 2 lines with-in the method called createRightHourStrip(). I&#8217;m not writing the entire body of that method instead I&#8217;m just writing a condition with in which these lines are written.</p>
<pre>if(ObjectUtil.dateCompare(obj.date, _savedDate) == 0)
{
    <strong>
	obj.hour = String(obj.hour);
	obj.hour = (obj.hour.length &lt; 2) ? ("0" + obj.hour) : obj.hour;
	</strong>
    if(obj.hour == strLabel &amp;&amp; obj.meridiem == objHourCell.data.meridiem)
    {
        if(obj.mins == 0)
        {
            objHourCell.btnFirstHalf.label = obj.desc;
        }
        else
        {
            objHourCell.btnSecondHalf.label = obj.desc;
        }
    }
}
</pre>
<p>In case if a value for the hour is stored as a single digit the below line will allow the program to convert it in to double digit.</p>
<p>Apart from this we have declared couple of new constants which are being used in the code. These constants are decal red in CustomEvents class. Here:</p>
<pre>public static const FETCH_DB_DATA:String = "fetchDBData";
public static const DP_CHANGED:String = "dpChanged";
</pre>
<h3>Step 7: Final Changes in DataHolder class</h3>
<p>There is a single line change in DataHolder class and that is only to dispatch an event of DP_CHANGED when we update <strong>dataProvider</strong> property with in it. To accomplish this we need to call below code under the setter of <strong>dataProvider</strong> property. This line has to at last position with in this setter method.</p>
<pre>dispatchEvent(new CustomEvents(CustomEvents.DP_CHANGED));</pre>
<h3>Conclusion</h3>
<p>Now you all know how to connect Flex Event Calendar with the Database to store calendar events in the database. In case any help is required feel free to contact me either by commenting or via my website <a href="http://www.ankur-arora.com" target="_blank" rel="nofollow">http://www.ankur-arora.com</a></p>
<p><center><script type="text/javascript">// <![CDATA[
google_ad_client = "pub-7847584538700437"; /* 300x250, created 5/7/10 - The Tech Labs */ google_ad_slot = "0549771992"; google_ad_width = 300; google_ad_height = 250;
// ]]&gt;</script><br />
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">// <![CDATA[

// ]]&gt;</script></center></p><p>The post <a href="https://www.thetechlabs.com/interfaces/connecting-flex-calendar-with-database-using-amf-php/">Connecting Flex Calendar With Database Using AMF-PHP</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.thetechlabs.com/interfaces/connecting-flex-calendar-with-database-using-amf-php/feed/</wfw:commentRss>
			<slash:comments>15</slash:comments>
		
		
			</item>
		<item>
		<title>Away3D Shoot&#8217;em&#8217;Up Tutorial &#8211; Powerups</title>
		<link>https://www.thetechlabs.com/3d/away3d-shootemup-tutorial-powerups/</link>
		
		<dc:creator><![CDATA[The Tech Labs]]></dc:creator>
		<pubDate>Sat, 20 Mar 2010 09:00:05 +0000</pubDate>
				<category><![CDATA[3D]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Games]]></category>
		<category><![CDATA[Tech Tutorials]]></category>
		<category><![CDATA[frameworks]]></category>
		<category><![CDATA[oop]]></category>
		<category><![CDATA[powerups]]></category>
		<category><![CDATA[web-development]]></category>
		<guid isPermaLink="false">https://www.thetechlabs.com/?p=1751</guid>

					<description><![CDATA[<p>In this article we add some powerups to the game.</p>
<p>The post <a href="https://www.thetechlabs.com/3d/away3d-shootemup-tutorial-powerups/">Away3D Shoot’em’Up Tutorial – Powerups</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>In this article we add some powerups to the game.</p>
<h3>Result</h3>
<p style="text-align: center;"><object width="540" height="360" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="data" value="https://www.thetechlabs.com/wp-content/uploads/2010/03/Powerups.swf" /><param name="quality" value="https://www.thetechlabs.com/wp-content/uploads/2010/03/Powerups.swf" /><param name="wmode" value="window" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2010/03/Powerups.swf" /><embed type="application/x-shockwave-flash" width="540" height="360" src="https://www.thetechlabs.com/wp-content/uploads/2010/03/Powerups.swf" wmode="window" quality="best" data="https://www.thetechlabs.com/wp-content/uploads/2010/03/Powerups.swf" /></object></p>
<p style="text-align: center;"><a title="Download Source Files" href="https://www.thetechlabs.com/wp-content/uploads/2010/03/Powerups-Source.zip" target="_self"><br />
<img decoding="async" class="style1" title="Download Source Files" src="https://www.thetechlabs.com/wp-content/uploads/2009/04/sourcefiles.jpg" alt="Download Source Files" width="246" height="93" /></a></p>
<h3>Requirements</h3>
<p><a href="http://www.adobe.com/products/flex/?promoid=BPDEQ" target="_blank" rel="nofollow">Adobe Flex v3.02</a></p>
<p><a href="http://away3d.com/downloads" target="_blank" rel="nofollow">Away3D v3.3.3 source code</a></p>
<p><a href="http://flintparticles.org/" target="_blank" rel="nofollow">Flint 2.1.0 source code</a></p>
<h3>Pre-Requisites</h3>
<p>You should read the previous articles in this series.</p>
<p><a href="https://www.thetechlabs.com/tutorials/3d/create-from-scratch-a-away3d-shootemup-game-part-1/" target="_blank">Create from Scratch a Away3D Shoot&#8217;em&#8217;Up Game: Part 1</a><br />
<a href="https://www.thetechlabs.com/tutorials/3d/create-from-scratch-a-away3d-shoot%E2%80%99em%E2%80%99up-game-part-2/" target="_blank">Create from Scratch a Away3D Shoot&#8217;em&#8217;Up Game: Part 2</a><br />
<a href="https://www.thetechlabs.com/tutorials/3d/create-from-scratch-a-away3d-shoot%E2%80%99em%E2%80%99up-game-part-3/" target="_blank">Create from Scratch a Away3D Shoot&#8217;em&#8217;Up Game: Part 3</a><br />
<a href="https://www.thetechlabs.com/tutorials/3d/create-from-scratch-a-away3d-shoot%E2%80%99em%E2%80%99up-game-part-4/" target="_blank">Create from Scratch a Away3D Shoot&#8217;em&#8217;Up Game: Part 4</a></p>
<h3>Powerups</h3>
<p>In the last article we implemented a new system for defining and creating the weapons. There was no way for the player to change his weapons though &#8211; for that we need to add powerups.</p>
<h3>Powerup.as</h3>
<pre>package
{
	import away3d.materials.BitmapMaterial;
	import away3d.primitives.Plane;

	public class Powerup extends MeshObject
	{
		protected static const POWERUP_SIZE:Number = 6;
		protected static const POWERUP_SPEED:Number = 30;
		protected static const POWERUP_X_LIMIT:Number = 70;
		protected static const POWERUP_Y_LIMIT:Number = 60;
		protected static const POWERUP_X_START:Number = 65;
		protected static const POWERUP_Y_START:Number = 50;
		protected var weapon:int = 0;
		protected var shields:int = 0;
		protected var score:int = 0;

		public function get Weapon():int
		{
			return weapon;
		}

		public function get Shields():int
		{
			return shields;
		}

		public function get Score():int
		{
			return score;
		}

		public function Powerup()
		{
			super();
		}

		protected function startupPowerup(engineManager:EngineManager, material:BitmapMaterial, weapon:int, score:int, shields:int):Powerup
		{
			var plane:Plane = new Plane(
				{material:material,
				width:POWERUP_SIZE,
				height:POWERUP_SIZE,
				yUp:false});
			super.startupMeshObject(engineManager, plane);

			this.collisionName = CollisionIdentifiers.POWERUP;
			this.model.x = POWERUP_X_LIMIT;
			this.model.y = MathUtils.randRange(-POWERUP_Y_START, POWERUP_Y_START);
			this.weapon = weapon;
			this.shields = shields;
			this.score = score;

			return this;
		}

		public function startupRandomPowerup(engineManager:EngineManager):Powerup
		{
			var random:int = int(Math.random() * 2);
			switch (random)
			{
				case 0:
					return startupWeapon1Powerup(engineManager);
				case 1:
					return startupWeapon2Powerup(engineManager);
			}

			return null;
		}

		public function startupWeapon1Powerup(engineManager:EngineManager):Powerup
		{
			return startupPowerup(
				engineManager,
				ResourceManager.Powerup1_Tex,
				1,
				0,
				0);
		}

		public function startupWeapon2Powerup(engineManager:EngineManager):Powerup
		{
			return startupPowerup(
				engineManager,
				ResourceManager.Powerup2_Tex,
				2,
				0,
				0);
		}

		override public function enterFrame(dt:Number):void
		{
			super.enterFrame(dt);

			this.model.x -= POWERUP_SPEED * dt;

			if (this.model.x &gt; POWERUP_X_LIMIT ||
				this.model.x &lt; -POWERUP_X_LIMIT || 				this.model.y &gt; POWERUP_Y_LIMIT ||
				this.model.y &lt; -POWERUP_Y_LIMIT)
				this.shutdown();
		}

		override public function collision(other:MeshObject):void
		{
			this.shutdown();
		}
	}
}
</pre>
<p>This code should look familiar. We have extended the MeshObject class, which allows us to put a mesh on the screen. Like the weapons and the enemies, the powerup mesh is a Plane orientated to face the camera, and textured with a BitmapMaterial embedded by the ResourceManager class. The collisionName has been set to CollisionIdentifiers.POWERUP to identify this as a powerup to the collision system. The enterFrame functions moves the Powerup across the screen until it is no longer visible, at which point it is shutdown.</p>
<p>The thing that makes the powerup unique are the three properties Shield, Score and Weapon. Each represents the reward that the player will receive when this powerup is collected. You would probably only create powerups that have one of these three properties set to something other than zero, but there is also no reason why you couldn&#8217;t have a powerup that gave the player points and some additional shields.</p>
<p>The Powerup class has a number of startup functions, along with a startup function that creates a random Powerup. You may recall from the last article that we spent some time breaking up the Weapon class to avoid using that one class to create multiple enemy types. In this case though, because the properties defined by the Powerup class are shared by all the different types of powerups (every powerup will have a shield, score and weapon property, and move in a straight line across the screen), there is no downside to having the Powerup startup functions create the various different types of powerups.</p>
<p>So here we have defined two powerups, one for weapon 1 (startupWeapon1Powerup), and for weapon 2 (startupWeapon2Powerup). The code for creating powerups for shields and points would be very similar.</p>
<h3>Player.as</h3>
<pre>package
{
	import away3d.core.math.Number3D;
	import away3d.events.MouseEvent3D;
	import away3d.primitives.*;

	import flash.events.*;
	import flash.geom.*;
	import flash.media.*;

	import mx.core.*;

	public class Player extends DamageableObject
	{
		protected static const PLAYER_X_LIMIT:Number = 52;
		protected static const PLAYER_Y_LIMIT:Number = 38;
		protected static const PLAYER_WIDTH:Number = 16;
		protected static const PLAYER_HEIGHT:Number = 5;
		protected static const COLLISION_PLANE_WIDTH:Number = 150;
		protected static const COLLISION_PLANE_HEIGHT:Number = 100;
		protected static const PLAYER_SHIELDS:int = 10;
		protected var collisionPlane:MeshObject = null;
		protected var shooting:Boolean = false;
		protected var timeToNextShot:Number = 0;

		public function Player()
		{
			super();
		}

		public function startupPlayer(engineManager:EngineManager):Player
		{
			var plane:Plane = new Plane(
				{material:ResourceManager.Player_Tex,
				width:PLAYER_WIDTH,
				height:PLAYER_HEIGHT,
				yUp:false});
			super.startupMeshObject(engineManager, plane);

			var collisionPlaneMesh:Plane = new Plane(
				{material:new NullMaterial(),
				width:COLLISION_PLANE_WIDTH,
				height:COLLISION_PLANE_HEIGHT,
				yUp:false});
			collisionPlane = new MeshObject().startupMeshObject(engineManager, collisionPlaneMesh);
			collisionPlaneMesh.addEventListener(MouseEvent3D.MOUSE_MOVE, this.mouseMove3D);

			this.collisionName = CollisionIdentifiers.PLAYER;
			this.shields = PLAYER_SHIELDS;

			Application.application.pbarLife.setProgress(this.shields, PLAYER_SHIELDS);

			return this;
		}

		override public function shutdown():void
		{
			collisionPlane.model.removeEventListener(MouseEvent3D.MOUSE_MOVE, this.mouseMove3D);
			collisionPlane.shutdown();
			collisionPlane = null;
			super.shutdown();
		}

		override public function enterFrame(dt:Number):void
		{
			super.enterFrame(dt);

			timeToNextShot -= dt;
			timeToNextShot = timeToNextShot&lt;0?0:timeToNextShot; 			if (timeToNextShot == 0 &amp;&amp; shooting) 			{ 				var weaponPosition:Number3D = new Number3D(); 				weaponPosition.add(this.model.position, new Number3D(PLAYER_WIDTH / 2, 0, 0)); 				 				timeToNextShot = this.engineManager.MyApplicationManager.MyWeaponManager.createWeapon( 					this.engineManager.MyApplicationManager.MyPlayerWeapon, 					this.engineManager.MyApplicationManager.MyPlayerWeaponLevel,  					weaponPosition); 			} 				 			if (this.model.x &gt; PLAYER_X_LIMIT)
				this.model.x = PLAYER_X_LIMIT;
			if (this.model.x &lt; -PLAYER_X_LIMIT) 				this.model.x = -PLAYER_X_LIMIT; 			 			if (this.model.y &gt; PLAYER_Y_LIMIT)
				this.model.y = PLAYER_Y_LIMIT;
			if (this.model.y &lt; -PLAYER_Y_LIMIT)
				this.model.y = -PLAYER_Y_LIMIT;
		}

		public function mouseMove3D(event:MouseEvent3D):void
		{
			if (this.model)
			{
				this.model.x = event.sceneX;
				this.model.y = event.sceneY;
			}
		}

		public override function collision(other:MeshObject):void
		{
			if (other.collisionName == CollisionIdentifiers.POWERUP)
			{
				var powerup:Powerup = other as Powerup;
				this.shields += powerup.Shields;
				this.engineManager.MyApplicationManager.MyScore += powerup.Score;
				if (powerup.Weapon != 0)
				{
					this.engineManager.MyApplicationManager.changePlayerWeapon(powerup.Weapon);
				}
			}
		}

		public override function mouseDown(event:MouseEvent):void
		{
			shooting = true;
		}

		public override function damage(amount:int):void
		{
			super.damage(amount);
			Application.application.pbarLife.setProgress(this.shields, PLAYER_SHIELDS);
		}

		public override function mouseUp(event:MouseEvent):void
		{
			shooting = false;
		}

		protected override function die():void
		{
			engineManager.MyApplicationManager.levelEnded();
			super.die();
		}
	}
}
</pre>
<p>The Player class needs to be modified to respond to a collision with a Powerup.</p>
<pre>if (other.collisionName == CollisionIdentifiers.POWERUP)
{
	var powerup:Powerup = other as Powerup;
	this.shields += powerup.Shields;
	this.engineManager.MyApplicationManager.MyScore += powerup.Score;
	if (powerup.Weapon != 0)
	{
		this.engineManager.MyApplicationManager.changePlayerWeapon(powerup.Weapon);
	}
}
</pre>
<p>In the collision function we test for a collision with a Powerup. When such a collision has been detected, the players shields and score are updated. If the Shields and Score properties have been set to zero then these updates have no effect.</p>
<p>If the Weapon property is not zero we ask the ApplicationManager to update the players weapons by calling the changePlayerWeapon function.</p>
<h3>ApplicationManager.as</h3>
<pre>package
{
	import mx.core.Application;

	public class ApplicationManager extends BaseObject
	{
		protected static const TIME_BETWEEN_BUILDINGS:Number = 2;
		protected static const TIME_BETWEEN_ENEMIES:Number = 1;
		protected static const TIME_BETWEEN_POWERUPS:Number = 10;
		protected static const TIME_TO_LEVEL_END:Number = 1;
		protected var timeToEndGame:Number = TIME_TO_LEVEL_END;
		protected var timeToNextBuilding:Number = 0;
		protected var timeToNextEnemy:Number = TIME_BETWEEN_ENEMIES;
		protected var timeToNextPowerup:Number = 0;//TIME_BETWEEN_POWERUPS;
		protected var levelHasEnded:Boolean = false;
		protected var myPlayerWeapon:int = 1;
		protected var myPlayerWeaponLevel:int = 1;
		protected var myWeaponManager:WeaponManager = null;
		protected var myScore:int = 0;

		public function get MyWeaponManager():WeaponManager
		{
			return myWeaponManager;
		}

		public function get MyPlayerWeapon():int
		{
			return myPlayerWeapon;
		}

		public function get MyPlayerWeaponLevel():int
		{
			return myPlayerWeaponLevel;
		}

		public function set MyScore(value:int):void
		{
			myScore = value;
			Application.application.lblScore.text = myScore;
		}

		public function get MyScore():int
		{
			return myScore;
		}

		public function ApplicationManager()
		{
			super();
		}

		public function startupApplicationManager(engineManager:EngineManager):ApplicationManager
		{
			this.startupBaseObject(engineManager);
			this.engineManager.addCollidingPair(CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYER);
			this.engineManager.addCollidingPair(CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYERWEAPON);
			this.engineManager.addCollidingPair(CollisionIdentifiers.PLAYER, CollisionIdentifiers.POWERUP);
			this.myWeaponManager = new WeaponManager().startupWeaponManager(this.engineManager);

			return this;
		}

		public function startLevel1():void
		{
			timeToNextBuilding = 0;
			MyScore = 0;

			new BackgroundPlane().startupBackgroundPlane(engineManager);
			new Player().startupPlayer(engineManager);

			// prepopulate the city
			for (var i:int = 0; i &lt; 5; ++i)
			{
				var building:BackgroundBuilding = new BackgroundBuilding();
				building.startupBackgroundBuilding(engineManager);
				building.enterFrame((i + 1) * 2);
			}
		}

		public function levelEnded():void
		{
			levelHasEnded = true;
		}

		public override function shutdown():void
		{
			super.shutdown();
		}

		public override function enterFrame(dt:Number):void
		{
			timeToNextBuilding -= dt;
			if (timeToNextBuilding &lt;= 0)
			{
				timeToNextBuilding = TIME_BETWEEN_BUILDINGS;
				new BackgroundBuilding().startupBackgroundBuilding(engineManager);
			}

			timeToNextEnemy -= dt;
			if (timeToNextEnemy &lt;= 0)
			{
				timeToNextEnemy = TIME_BETWEEN_ENEMIES;
				new Enemy().startupBasicEnemy(engineManager);
			}

			timeToNextPowerup -= dt;
			if (timeToNextPowerup &lt;= 0)
			{
				timeToNextPowerup = TIME_BETWEEN_POWERUPS;
				new Powerup().startupRandomPowerup(engineManager);
			}

			if (levelHasEnded)
			{
				timeToEndGame -= dt;
				if (timeToEndGame &lt;= 0)
				{
					engineManager.nextStateChange = "MainMenu";
				}
			}
		}

		public function changePlayerWeapon(weapon:int):void
		{
			if (weapon == this.myPlayerWeapon &amp;&amp;
				this.myWeaponManager.isValidWeapon(
					this.myPlayerWeapon,
					this.myPlayerWeaponLevel + 1))
			{
				++this.myPlayerWeaponLevel;
			}
			else if (this.myWeaponManager.isValidWeapon(weapon, 1))
			{
				this.myPlayerWeapon = weapon;
				while (!this.myWeaponManager.isValidWeapon(
					this.myPlayerWeapon,
					this.myPlayerWeaponLevel))
				{
					--this.myPlayerWeaponLevel;
				}
			}
		}
	}
}
</pre>
<p>In order for the players weapons to change in response to a powerup, the ApplicationManager gets a new function called changePlayerWeapon.</p>
<pre>public function changePlayerWeapon(weapon:int):void
{
	if (weapon == this.myPlayerWeapon &amp;&amp;
		this.myWeaponManager.isValidWeapon(
			this.myPlayerWeapon,
			this.myPlayerWeaponLevel + 1))
	{
		++this.myPlayerWeaponLevel;
	}</pre>
<p>If the player has collected a powerup for the current weapon (i.e. the powerups Weapon property equals the ApplicationManagers myPlayerWeapon property) the ApplicationManager will attempt to increase the weapon level. Obviously this can only be done if the current weapon has not been maxed out, so a call to the WeaponManager isValidWeapon function is used to see if such an upgrade is possible.</p>
<pre>	else if (this.myWeaponManager.isValidWeapon(weapon, 1))
	{
		this.myPlayerWeapon = weapon;
		while (!this.myWeaponManager.isValidWeapon(
			this.myPlayerWeapon,
			this.myPlayerWeaponLevel))
		{
			--this.myPlayerWeaponLevel;
		}
	}
}</pre>
<p>If the player has collected a powerup for a new weapon then the ApplicationManager changes to that weapon. Because weapons may have different levels defined (weapon 1 may have 4 levels, whereas weapon 2 may only have 3), the ApplicationManager will downgrade the weapon level where necessary.</p>
<pre>timeToNextPowerup -= dt;
if (timeToNextPowerup &lt;= 0)
{
	timeToNextPowerup = TIME_BETWEEN_POWERUPS;
	new Powerup().startupRandomPowerup(engineManager);
}
</pre>
<p>The Powerups are added to the game just like the enemies and background buildings. The ApplicationManager is set to add one powerup after a specified amount of time has passed in the enterFrame function.</p>
<h3>WeaponManager.as</h3>
<pre>package
{
	import away3d.core.math.Number3D;
	import away3d.materials.BitmapMaterial;

	import flash.utils.Dictionary;

	public class WeaponManager
	{
		protected var engineManager:EngineManager = null;
		protected var weaponDatabase:Dictionary = new Dictionary();

		public function WeaponManager()
		{
			super();
		}

		public function startupWeaponManager(engineManager:EngineManager):WeaponManager
		{
			this.engineManager = engineManager;

			var weapon1:Dictionary = new Dictionary();
			weaponDatabase[1] = weapon1;

			weapon1[1] = weapon1Level1;
			weapon1[2] = weapon1Level2;

			var weapon2:Dictionary = new Dictionary();
			weaponDatabase[2] = weapon2;

			weapon2[1] = weapon2Level1;
			weapon2[2] = weapon2Level2;

			return this;
		}

		public function createWeapon(weapon:int, weaponLevel:int, position:Number3D):Number
		{
			if (isValidWeapon(weapon, weaponLevel))
			{
				return weaponDatabase[weapon][weaponLevel](position);
			}

			return 0;
		}

		public function isValidWeapon(weapon:int, weaponLevel:int):Boolean
		{
			if (weaponDatabase[weapon] != null)
			{
				if (weaponDatabase[weapon][weaponLevel] != null)
					return true;
			}

			return false;
		}

		public function weapon1Level1(weaponPosition:Number3D):Number
		{
			var material:BitmapMaterial = ResourceManager.Bullet_Tex;
			var timeToNextShot:Number = 0.4;
			var damage:int = 1;
			var xDir:Number = 1;
			var yDir:Number = 0;
			var speed:Number = 100;
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, material, weaponPosition, damage, xDir, yDir, speed);

			new SoundEffect().startupSoundEffect(this.engineManager, ResourceManager.Gun1FX);

			return timeToNextShot;
		}

		public function weapon1Level2(weaponPosition:Number3D):Number
		{
			var timeToNextShot:Number = 0.4;

			var material:BitmapMaterial = ResourceManager.Bullet_Tex;
			var damage:int = 1;
			var xDir:Number = 1;
			var yDir:Number = 0;
			var yDir2:Number = 0.5;
			var yDir3:Number = -0.5;
			var speed:Number = 100;
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, material, weaponPosition, damage, xDir, yDir, speed);
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, material, weaponPosition, damage, xDir, yDir2, speed);
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, material, weaponPosition, damage, xDir, yDir3, speed);

			new SoundEffect().startupSoundEffect(this.engineManager, ResourceManager.Gun1FX);

			return timeToNextShot;
		}

		public function weapon2Level1(weaponPosition:Number3D):Number
		{
			var material:BitmapMaterial = ResourceManager.Bullet2_Tex;
			var timeToNextShot:Number = 1;
			var damage:int = 2;
			var xDir:Number = 1;
			var yDir:Number = 0;
			var speed:Number = 100;
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, material, weaponPosition, damage, xDir, yDir, speed);

			new SoundEffect().startupSoundEffect(this.engineManager, ResourceManager.Gun1FX);

			return timeToNextShot;
		}

		public function weapon2Level2(weaponPosition:Number3D):Number
		{
			var material:BitmapMaterial = ResourceManager.Bullet2_Tex;
			var timeToNextShot:Number = 1;
			var damage:int = 1;
			var xDir:Number = 1;
			var yDir:Number = 0;
			var yDir2:Number = 0.5;
			var yDir3:Number = -0.5;
			var speed:Number = 100;
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, material, weaponPosition, damage, xDir, yDir, speed);
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, material, weaponPosition, damage, xDir, yDir2, speed);
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, material, weaponPosition, damage, xDir, yDir3, speed);

			new SoundEffect().startupSoundEffect(this.engineManager, ResourceManager.Gun1FX);

			return timeToNextShot;
		}

	}
}
</pre>
<p>In order for the player to have weapons to powerup to, we need to add some more weapon definitions to the WeaponManager.</p>
<pre>var weapon2:Dictionary = new Dictionary();
weaponDatabase[2] = weapon2;

weapon2[1] = weapon2Level1;
weapon2[2] = weapon2Level2;
</pre>
<p>The weapon2Level1 and weapon2Level2 functions are added to the weaponDatabase in the startupWeaponManager function. The code for doing this is exactly the same as the code that added the original weapon definitions. This is one of the benefits of having all the weapon creation code separated into it&#8217;s own class. With a few lines of code, and a few simple functions, we can create new weapon definitions, without a whole mess of if/else/switch statements in the middle of the Players enterFrame function.</p>
<p>The code required to implement the powerups has been quite straight forward. As we create the base framework additional functionality, like powerups, becomes quite easy to add to the game. In the next article we will look at finalising this base framework by moving away from the ApplicationManager creating an endless number of enemies towards specifically defined the levels.</p><p>The post <a href="https://www.thetechlabs.com/3d/away3d-shootemup-tutorial-powerups/">Away3D Shoot’em’Up Tutorial – Powerups</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Away3D Shoot&#8217;em&#8217;Up Tutorial &#8211; Sound Effects</title>
		<link>https://www.thetechlabs.com/3d/away3d-shootemup-tutorial-sound-effects/</link>
					<comments>https://www.thetechlabs.com/3d/away3d-shootemup-tutorial-sound-effects/#comments</comments>
		
		<dc:creator><![CDATA[The Tech Labs]]></dc:creator>
		<pubDate>Mon, 15 Feb 2010 14:59:08 +0000</pubDate>
				<category><![CDATA[3D]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Games]]></category>
		<category><![CDATA[Tech Tutorials]]></category>
		<category><![CDATA[frameworks]]></category>
		<category><![CDATA[oop]]></category>
		<category><![CDATA[sound effects]]></category>
		<category><![CDATA[web-development]]></category>
		<guid isPermaLink="false">https://www.thetechlabs.com/?p=1692</guid>

					<description><![CDATA[<p>In this article we add some sound effects to the game.</p>
<p>The post <a href="https://www.thetechlabs.com/3d/away3d-shootemup-tutorial-sound-effects/">Away3D Shoot’em’Up Tutorial – Sound Effects</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>In this article we add some sound effects to the game.</p>
<h3>Result</h3>
<p style="text-align: center;"><object data="https://www.thetechlabs.com/wp-content/uploads/2010/02/SoundEffects.swf" type="application/x-shockwave-flash" width="545" height="400"><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2010/02/SoundEffects.swf" /></object></p>
<p style="text-align: center;"><a title="Download Source Files" href="https://www.thetechlabs.com/wp-content/uploads/2010/02/SoundEffects-Source.zip" target="_self"><img decoding="async" class="size-full wp-image-466 aligncenter" title="Download Source Files" src="https://www.thetechlabs.com/wp-content/uploads/2009/04/sourcefiles.jpg" alt="Download Source Files" width="246" height="93" /></a></p>
<h3>Requirements</h3>
<p><a href="http://www.adobe.com/products/flex/?promoid=BPDEQ" target="_blank" rel="nofollow">Adobe Flex v3.02</a></p>
<p><a href="http://away3d.com/downloads" target="_blank" rel="nofollow">Away3D v3.3.3 source code</a></p>
<p><a href="http://flintparticles.org/" target="_blank" rel="nofollow">Flint 2.1.0 source code</a></p>
<h3>Pre-Requisites</h3>
<p>You should read the previous articles in this series.</p>
<p><a href="https://www.thetechlabs.com/tutorials/3d/create-from-scratch-a-away3d-shootemup-game-part-1/" target="_blank">Create from Scratch a Away3D Shoot&#8217;em&#8217;Up Game: Part 1</a><br />
<a href="https://www.thetechlabs.com/tutorials/3d/create-from-scratch-a-away3d-shoot%E2%80%99em%E2%80%99up-game-part-2/" target="_blank">Create from Scratch a Away3D Shoot&#8217;em&#8217;Up Game: Part 2</a><br />
<a href="https://www.thetechlabs.com/tutorials/3d/create-from-scratch-a-away3d-shoot%E2%80%99em%E2%80%99up-game-part-3/" target="_blank">Create from Scratch a Away3D Shoot&#8217;em&#8217;Up Game: Part 3</a><br />
<a href="https://www.thetechlabs.com/tutorials/3d/create-from-scratch-a-away3d-shoot%E2%80%99em%E2%80%99up-game-part-4/" target="_blank">Create from Scratch a Away3D Shoot&#8217;em&#8217;Up Game: Part 4</a></p>
<h3>Sound Effects</h3>
<p>I remember playing Unreal when it was first released. The sniper rifle was one of my <span lang="en-au">favourite</span> weapons in multiplayer, but it sounded weak. Thankfully a patch was released that, among other things, gave the sniper rifle a much meatier sound effect. My frag count went up just because I loved the new sound so much.</p>
<p>Sound effects can add a whole new dimension to a game, and here we will look at how to get some sounds into our shoot&#8217;em&#8217;up.</p>
<h3>ResourceManager.as</h3>
<pre>package
{
	import away3d.materials.BitmapMaterial;
	import away3d.materials.TransformBitmapMaterial;

	import mx.core.BitmapAsset;
	import mx.core.SoundAsset;

	public class ResourceManager
	{
		[Embed (source="../media/building1.png")]
		public static const Building1:Class;
		public static const Building1_BitmapAsset:BitmapAsset = new Building1();
		public static const Building1_Tex:BitmapMaterial = new BitmapMaterial(Building1_BitmapAsset.bitmapData, {smooth:true});

		[Embed (source="../media/building2.png")]
		public static const Building2:Class;
		public static const Building2_BitmapAsset:BitmapAsset = new Building2();
		public static const Building2_Tex:BitmapMaterial = new BitmapMaterial(Building2_BitmapAsset.bitmapData, {smooth:true});

		[Embed (source="../media/building3.png")]
		public static const Building3:Class;
		public static const Building3_BitmapAsset:BitmapAsset = new Building3();
		public static const Building3_Tex:BitmapMaterial = new BitmapMaterial(Building3_BitmapAsset.bitmapData, {smooth:true});

		[Embed (source="../media/roof1.png")]
		public static const Roof1:Class;
		public static const Roof1_BitmapAsset:BitmapAsset = new Roof1();
		public static const Roof1_Tex:BitmapMaterial = new BitmapMaterial(Roof1_BitmapAsset.bitmapData, {smooth:true});

		[Embed (source="../media/background1.png")]
		public static const Background1:Class;
		public static const Background1_BitmapAsset:BitmapAsset = new Background1();
		public static const Background1_Tex:TransformBitmapMaterial = new TransformBitmapMaterial(Background1_BitmapAsset.bitmapData, {smooth:true});

		[Embed (source="../media/player.png")]
		public static const Player:Class;
		public static const Player_BitmapAsset:BitmapAsset = new Player();
		public static const Player_Tex:BitmapMaterial = new BitmapMaterial(Player_BitmapAsset.bitmapData);

		[Embed (source="../media/enemy.png")]
		public static const Enemy:Class;
		public static const Enemy_BitmapAsset:BitmapAsset = new Enemy();
		public static const Enemy_Tex:BitmapMaterial = new BitmapMaterial(Enemy_BitmapAsset.bitmapData);

		[Embed (source="../media/bullet.png")]
		public static const Bullet:Class;
		public static const Bullet_BitmapAsset:BitmapAsset = new Bullet();
		public static const Bullet_Tex:BitmapMaterial = new BitmapMaterial(Bullet_BitmapAsset.bitmapData);

		[Embed(source="../media/gun1.mp3")]
		public static var Gun1Sound:Class;
		public static var Gun1FX:SoundAsset = new Gun1Sound() as SoundAsset;

		[Embed(source="../media/explosion.mp3")]
		public static var ExplosionSound:Class;
		public static var ExplosionFX:SoundAsset = new ExplosionSound() as SoundAsset;
	}
}
</pre>
<p>Sounds are embedded into the SWF file just like images. Here we embed two MP3 files, and create new SoundAssets from the embedded resources. It&#8217;s these SoundAsset objects that we will use to play the sound effects. Because Flash has built in support for MP3, we don&#8217;t have to worry about decoding the files before they are played.</p>
<p>As you might have guessed from the names of the sound files, these two sound effects will be played when there is an explosion, and when a weapon is fired. The weapons have been very simple to this point, with the Player creating a new instance of the Weapon class directly when it is time to shoot. This setup does not easily allow the player to have different types of weapons, or to be able to create multiple weapons with every shot. Since the sound effects will be played when the weapons are fired, we first need to create a more flexible system for the weapons.</p>
<p>The Weapon class used to include a logic property, which was a reference to a function to be called during the enterFrame function. My idea for this was to allow the Weapon class to create multiple varieties of weapons, where the function pointed to by the logic property would define how the weapon behaved, and the startup function would initialise the necessary properties. When I looked at this again I realised that the Weapon class would end up having numerous properties that related to only one type of weapon e.g. the simple weapon would have a direction property, and a homing missile might have the a nearest enemy property. This would quickly clutter the Weapon class.</p>
<p>By designing the Weapon class in a way that allowed it to create multiple weapon variations I had hoped to save myself some typing by not having to create a whole bunch of subclasses. It&#8217;s a lesson I keep learning over and over: shortcuts will bite you in the ass later on. So here the Weapon class is stripped down to include just the basic functionality common to all weapons.</p>
<h3>Weapon.as</h3>
<pre>package
{
	import away3d.core.math.Number3D;
	import away3d.materials.BitmapMaterial;
	import away3d.primitives.Plane;

	public class Weapon extends MeshObject
	{
		protected static const WEAPON_X_LIMIT:Number = 70;
		protected static const WEAPON_Y_LIMIT:Number = 60;
		protected var damage:int = 0;

		public function Weapon()
		{
			super();
		}

		public override function shutdown():void
		{
			super.shutdown();
		}

		public function startupWeapon(engineManager:EngineManager, material:BitmapMaterial, position:Number3D, sizeX:Number, sizeY:Number, damage:int):Weapon
		{
			var plane:Plane = new Plane(
				{material:material,
				width:sizeX,
				height:sizeY,
				yUp:false});
			super.startupMeshObject(engineManager, plane);
			this.model.position = position;
			this.damage = damage;

			return this;
		}

		public override function enterFrame(dt:Number):void
		{
			if (this.model.x &gt; WEAPON_X_LIMIT ||
				this.model.x &lt; -WEAPON_X_LIMIT || 				this.model.y &gt; WEAPON_Y_LIMIT ||
				this.model.y &lt; -WEAPON_Y_LIMIT)
				this.shutdown();
		}

		public override function collision(other:MeshObject):void
		{
			if (other.collisionName == CollisionIdentifiers.ENEMY ||
				other.collisionName == CollisionIdentifiers.PLAYER)
				(other as DamageableObject).damage(this.damage);
			this.shutdown();
		}
	}
}</pre>
<p>A new class, BasicWeapon, is created to define weapons that just move in a straight line. Even though it might not seem like a big deal, splitting the Weapon class in two will make the code a lot more manageable as more weapon types are added.</p>
<h3>BasicWeapon.as</h3>
<pre>package
{
	import away3d.core.math.Number3D;

	public class BasicWeapon extends Weapon
	{
		protected static const WEAPON_SIZE:Number = 2;
		protected var speed:Number = 0;
		protected var direction:Number3D = null;

		public function BasicWeapon()
		{
			super();
		}

		protected function startupBasicWeapon(engineManager:EngineManager, position:Number3D, damage:int, xDir:Number, yDir:Number, speed:Number):BasicWeapon
		{
			this.startupWeapon(engineManager, ResourceManager.Bullet_Tex, position, WEAPON_SIZE, WEAPON_SIZE, damage);
			this.direction = new Number3D(xDir, yDir, 0);
			this.direction.normalize();
			this.speed = speed;
			return this;
		}

		public function startupBasicPlayerWeapon(engineManager:EngineManager, position:Number3D, damage:int, xDir:Number, yDir:Number, speed:Number):BasicWeapon
		{
			startupBasicWeapon(engineManager, position, damage, xDir, yDir, speed);
			this.collisionName = CollisionIdentifiers.PLAYERWEAPON;
			return this;
		}

		public function startupBasicEnemyWeapon(engineManager:EngineManager, position:Number3D, damage:int, xDir:Number, yDir:Number, speed:Number):BasicWeapon
		{
			startupBasicWeapon(engineManager, position, damage, xDir, yDir, speed);
			this.collisionName = CollisionIdentifiers.ENEMYWEAPON;
			return this;
		}

		public override function enterFrame(dt:Number):void
		{
			this.model.x += speed * dt;

			super.enterFrame(dt);
		}

	}
}
</pre>
<p>In order to easily define a selection of weapons for the player and the enemies to fire at each other, we need to separate the code for the weapon creation out of the Player class. Instead this code will be placed in a new class called WeaponManager.</p>
<h3>WeaponManager.as</h3>
<pre>package
{
	import away3d.core.math.Number3D;

	import flash.utils.Dictionary;

	public class WeaponManager
	{
		protected var engineManager:EngineManager = null;
		protected var weaponDatabase:Dictionary = new Dictionary();

		public function WeaponManager()
		{
			super();
		}

		public function startupWeaponManager(engineManager:EngineManager):WeaponManager
		{
			this.engineManager = engineManager;

			var weapon1:Dictionary = new Dictionary();
			weaponDatabase[1] = weapon1;

			weapon1[1] = weapon1Level1;
			weapon1[2] = weapon1Level2;

			return this;
		}

		public function createWeapon(weapon:int, weaponLevel:int, position:Number3D):Number
		{
			if (isValidWeapon(weapon, weaponLevel))
			{
				return weaponDatabase[weapon][weaponLevel](position);
			}

			return 0;
		}

		public function isValidWeapon(weapon:int, weaponLevel:int):Boolean
		{
			if (weaponDatabase[weapon] != null)
			{
				if (weaponDatabase[weapon][weaponLevel] != null)
					return true;
			}

			return false;
		}

		public function weapon1Level1(weaponPosition:Number3D):Number
		{
			var timeToNextShot:Number = 0.4;
			var damage:int = 1;
			var xDir:Number = 1;
			var yDir:Number = 0;
			var speed:Number = 100;
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, weaponPosition, damage, xDir, yDir, speed);

			new SoundEffect().startupSoundEffect(this.engineManager, ResourceManager.Gun1FX);

			return timeToNextShot;
		}

		public function weapon1Level2(weaponPosition:Number3D):Number
		{
			var timeToNextShot:Number = 0.4;

			var damage:int = 1;
			var xDir:Number = 1;
			var yDir:Number = 0;
			var yDir2:Number = 1;
			var yDir3:Number = -1;
			var speed:Number = 100;
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, weaponPosition, damage, xDir, yDir, speed);
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, weaponPosition, damage, xDir, yDir2, speed);
			new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, weaponPosition, damage, xDir, yDir3, speed);

			new SoundEffect().startupSoundEffect(this.engineManager, ResourceManager.Gun1FX);

			return timeToNextShot;
		}

	}
}
</pre>
<p>Each weapon is defined by a number and a level. So weapon number 1 level 1 are simple bullets that move in a straight line. Weapon 1 level 2 might be three bullets fired out by the player.</p>
<p>The weaponDatabase property is a dictionary of weapon numbers mapped to another dictionary. This second dictionary contains the weapon levels mapped to functions. So by calling weaponDatabase[1][2]() you are calling the function that is mapped to weapon number 1 level 2.</p>
<p>For convenience we will refer to a weapon definition as the combination of the weapon number and level, and the functions that are called to create these weapon definitions will be referred to as weapon definition functions.</p>
<p>For this demo we have two weapon definitions, and to them we have mapped two weapon definition functions: weapon1level1 and weapon1level2. The code to populate the weaponDatabase property is in the startupWeaponManager function.</p>
<pre>var weapon1:Dictionary = new Dictionary();
weaponDatabase[1] = weapon1;
</pre>
<p>First we create a new Dictionarty to reperent the weapon number, and assign it to the weaponDatabase dictionary.</p>
<pre>weapon1[1] = weapon1Level1;
weapon1[2] = weapon1Level2;
</pre>
<p>Next the two weapon definition functions are mapped to the appropiate weapon levels.</p>
<pre>public function weapon1Level1(weaponPosition:Number3D):Number
{
	var timeToNextShot:Number = 0.4;
	var damage:int = 1;
	var xDir:Number = 1;
	var yDir:Number = 0;
	var speed:Number = 100;
	new BasicWeapon().startupBasicPlayerWeapon(this.engineManager, weaponPosition, damage, xDir, yDir, speed);

	new SoundEffect().startupSoundEffect(this.engineManager, ResourceManager.Gun1FX);

	return timeToNextShot;
}
</pre>
<p>The weapon definition functions are quite simple. The variables that will be supplied to the Weapon startup function (the BasicWeapon startupBasicPlayerWeapon function in this case) are defined so they can be clearly identified, and then the weapon itself is created. This code is the equivalent of what used to be in the Player class when the Player created the weapons directly. These functions return the time that should elapse before the player or an enemy fires again.</p>
<p>You will also note that we are creating a new SoundEffect object along with the weapon. The SoundEffect class will be discussed later.</p>
<pre>public function createWeapon(weapon:int, weaponLevel:int, position:Number3D):Number
{
	if (isValidWeapon(weapon, weaponLevel))
	{
		return weaponDatabase[weapon][weaponLevel](position);
	}

	return 0;
}

public function isValidWeapon(weapon:int, weaponLevel:int):Boolean
{
	if (weaponDatabase[weapon] != null)
	{
		if (weaponDatabase[weapon][weaponLevel] != null)
			return true;
	}

	return false;
}
</pre>
<p>The createWeapon and isValidWeapon functions provide an easy way to call the weapon definition functions by supplying the weapon number and weapon level, as well as the position that the weapon should be initially created at.</p>
<p>We have only two weapon definitions here, but you can imagine how much code there would be if we had 10 weapons each with 10 levels. That code would make the Player class a complete mess, so it makes sense to separate it out into the WeaponManager class.</p>
<h3>Player.as</h3>
<pre>package
{
	import away3d.core.math.Number3D;
	import away3d.events.MouseEvent3D;
	import away3d.primitives.*;

	import flash.events.*;
	import flash.geom.*;
	import flash.media.*;

	import mx.core.*;

	public class Player extends DamageableObject
	{
		protected static const PLAYER_X_LIMIT:Number = 52;
		protected static const PLAYER_Y_LIMIT:Number = 38;
		protected static const PLAYER_WIDTH:Number = 16;
		protected static const PLAYER_HEIGHT:Number = 5;
		protected static const COLLISION_PLANE_WIDTH:Number = 150;
		protected static const COLLISION_PLANE_HEIGHT:Number = 100;
		protected static const PLAYER_SHIELDS:int = 10;
		protected var collisionPlane:MeshObject = null;
		protected var shooting:Boolean = false;
		protected var timeToNextShot:Number = 0;

		public function Player()
		{
			super();
		}

		public function startupPlayer(engineManager:EngineManager):Player
		{
			var plane:Plane = new Plane(
				{material:ResourceManager.Player_Tex,
				width:PLAYER_WIDTH,
				height:PLAYER_HEIGHT,
				yUp:false});
			super.startupMeshObject(engineManager, plane);

			var collisionPlaneMesh:Plane = new Plane(
				{material:new NullMaterial(),
				width:COLLISION_PLANE_WIDTH,
				height:COLLISION_PLANE_HEIGHT,
				yUp:false});
			collisionPlane = new MeshObject().startupMeshObject(engineManager, collisionPlaneMesh);
			collisionPlaneMesh.addEventListener(MouseEvent3D.MOUSE_MOVE, this.mouseMove3D);

			this.collisionName = CollisionIdentifiers.PLAYER;
			this.shields = PLAYER_SHIELDS;

			Application.application.pbarLife.setProgress(this.shields, PLAYER_SHIELDS);

			return this;
		}

		override public function shutdown():void
		{
			collisionPlane.model.removeEventListener(MouseEvent3D.MOUSE_MOVE, this.mouseMove3D);
			collisionPlane.shutdown();
			collisionPlane = null;
			super.shutdown();
		}

		override public function enterFrame(dt:Number):void
		{
			super.enterFrame(dt);

			timeToNextShot -= dt;
			timeToNextShot = timeToNextShot&lt;0?0:timeToNextShot; 			if (timeToNextShot == 0 &amp;&amp; shooting) 			{ 				var weaponPosition:Number3D = new Number3D(); 				weaponPosition.add(this.model.position, new Number3D(PLAYER_WIDTH / 2, 0, 0)); 				 				timeToNextShot = this.engineManager.MyApplicationManager.MyWeaponManager.createWeapon( 					this.engineManager.MyApplicationManager.MyPlayerWeapon, 					this.engineManager.MyApplicationManager.MyPlayerWeaponLevel,  					weaponPosition); 			} 				 			if (this.model.x &gt; PLAYER_X_LIMIT)
				this.model.x = PLAYER_X_LIMIT;
			if (this.model.x &lt; -PLAYER_X_LIMIT) 				this.model.x = -PLAYER_X_LIMIT; 			 			if (this.model.y &gt; PLAYER_Y_LIMIT)
				this.model.y = PLAYER_Y_LIMIT;
			if (this.model.y &lt; -PLAYER_Y_LIMIT)
				this.model.y = -PLAYER_Y_LIMIT;
		}

		public function mouseMove3D(event:MouseEvent3D):void
		{
			if (this.model)
			{
				this.model.x = event.sceneX;
				this.model.y = event.sceneY;
			}
		}

		public override function collision(other:MeshObject):void
		{

		}

		public override function mouseDown(event:MouseEvent):void
		{
			shooting = true;
		}

		public override function damage(amount:int):void
		{
			super.damage(amount);
			Application.application.pbarLife.setProgress(this.shields, PLAYER_SHIELDS);
		}

		public override function mouseUp(event:MouseEvent):void
		{
			shooting = false;
		}

		protected override function die():void
		{
			engineManager.MyApplicationManager.levelEnded();
			super.die();
		}
	}
}
</pre>
<p>With the weapon creation code now in the WeaponManager class, the Player has to be modified to call the createWeapon function instead of creating the weapon directly. The weapon number and weapon level properties are kept in the ApplicationManager.</p>
<pre>timeToNextShot = this.engineManager.MyApplicationManager.MyWeaponManager.createWeapon(
		this.engineManager.MyApplicationManager.MyPlayerWeapon,
		this.engineManager.MyApplicationManager.MyPlayerWeaponLevel,
		weaponPosition);
</pre>
<h3>ApplicationManager.as</h3>
<pre>package
{
	import mx.core.Application;

	public class ApplicationManager extends BaseObject
	{
		protected static const TIME_BETWEEN_BUILDINGS:Number = 2;
		protected static const TIME_BETWEEN_ENEMIES:Number = 1;
		protected static const TIME_TO_LEVEL_END:Number = 1;
		protected var timeToEndGame:Number = TIME_TO_LEVEL_END;
		protected var timeToNextBuilding:Number = 0;
		protected var timeToNextEnemy:Number = TIME_BETWEEN_ENEMIES;
		protected var levelHasEnded:Boolean = false;
		protected var myPlayerWeapon:int = 1;
		protected var myPlayerWeaponLevel:int = 1;
		protected var myWeaponManager:WeaponManager = null;
		public var myScore:int = 0;

		public function get MyWeaponManager():WeaponManager
		{
			return myWeaponManager;
		}

		public function get MyScore():int
		{
			return myScore;
		}

		public function get MyPlayerWeapon():int
		{
			return myPlayerWeapon;
		}

		public function get MyPlayerWeaponLevel():int
		{
			return myPlayerWeaponLevel;
		}

		public function set score(value:int):void
		{
			myScore = value;
			Application.application.lblScore.text = myScore;
		}

		public function ApplicationManager()
		{
			super();
		}

		public function startupApplicationManager(engineManager:EngineManager):ApplicationManager
		{
			this.startupBaseObject(engineManager);
			this.engineManager.addCollidingPair(CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYER);
			this.engineManager.addCollidingPair(CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYERWEAPON);
			this.myWeaponManager = new WeaponManager().startupWeaponManager(this.engineManager);

			return this;
		}

		public function startLevel1():void
		{
			timeToNextBuilding = 0;
			score = 0;

			new BackgroundPlane().startupBackgroundPlane(engineManager);
			new Player().startupPlayer(engineManager);

			// prepopulate the city
			for (var i:int = 0; i &lt; 5; ++i)
			{
				var building:BackgroundBuilding = new BackgroundBuilding();
				building.startupBackgroundBuilding(engineManager);
				building.enterFrame((i + 1) * 2);
			}
		}

		public function levelEnded():void
		{
			levelHasEnded = true;
		}

		public override function shutdown():void
		{
			super.shutdown();
		}

		public override function enterFrame(dt:Number):void
		{
			timeToNextBuilding -= dt;
			if (timeToNextBuilding &lt;= 0)
			{
				timeToNextBuilding = TIME_BETWEEN_BUILDINGS;
				new BackgroundBuilding().startupBackgroundBuilding(engineManager);
			}

			timeToNextEnemy -= dt;
			if (timeToNextEnemy &lt;= 0)
			{
				timeToNextEnemy = TIME_BETWEEN_ENEMIES;
				new Enemy().startupBasicEnemy(engineManager);
			}

			if (levelHasEnded)
			{
				timeToEndGame -= dt;
				if (timeToEndGame &lt;= 0)
				{
					engineManager.nextStateChange = "MainMenu";
				}
			}
		}
	}
}</pre>
<p>The ApplicationManager creates the WeaponManager in the startupApplicationManager function, and holds the players weapon definition in the MyPlayerWeapon and MyPlayerWeaponLevel properties.</p>
<p>For a tutorial on adding sound effects we have focused a lot on how the weapons are created. But fear not, now that we have cleaned up the weapon definitions and the code to create these<br />
weapons, we can now add some sound effects.</p>
<h3>SoundEffect.as</h3>
<pre>package
{
	import flash.events.Event;
	import flash.media.SoundChannel;

	import mx.core.SoundAsset;

	public class SoundEffect extends BaseObject
	{
		protected var channel:SoundChannel = null;

		public function SoundEffect()
		{
			super();
		}

		public function startupSoundEffect(engineManager:EngineManager, sound:SoundAsset):SoundEffect
		{
			super.startupBaseObject(engineManager);

			channel = sound.play();
			channel.addEventListener(Event.SOUND_COMPLETE, soundComplete);
			return this;
		}

		override public function shutdown():void
		{
			if (channel != null)
			{
				channel.removeEventListener(Event.SOUND_COMPLETE, soundComplete);
				channel.stop();
				channel = null;
			}

			super.shutdown();
		}

		public function soundComplete(event:Event):void
		{
			shutdown();
		}

	}
}</pre>
<p>Playing sound effects is actually very simple. The SoundAsset class (created in the ResourceManager) has a play function which returns a SoundChannel object. The function of the SoundEffect class is to play the SoundAsset, and stop the sound when the SoundEffect is shutdown.</p>
<pre>public function startupSoundEffect(sound:SoundAsset):SoundEffect
{
	super.startupBaseObject();

	channel = sound.play();
	channel.addEventListener(Event.SOUND_COMPLETE, soundComplete);
	return this;
}
</pre>
<p>The startupSoundEffect takes the SoundAsset to be played, stores the SoundChannel that is returned when the SoundAsset is played, and watches for when the sound has finished playing.</p>
<pre>public function soundComplete(event:Event):void
{
	shutdown();
}
</pre>
<p>The soundComplete function is called when the sound has finished playing. It just calls shutdown.</p>
<pre>override public function shutdown():void
{
	if (channel != null)
	{
		channel.removeEventListener(Event.SOUND_COMPLETE, soundComplete);
		channel.stop();
		channel = null;
	}

	super.shutdown();
}
</pre>
<p>When the SoundEffect is shutdown, either because the sound has finished playing, or the EngineManager has called the shutdown function when changing states, the event listener is removed and the sound effect is stopped.</p>
<p>The SoundEffect class provides a kind of &#8220;fire and forget&#8221; way to create a sound effect. When you create it the sound will play, and it will clean itself up when the sound effect has finished, or, by virtue of that fact that it extends the BaseObject class, the EngineManager will clean it up when the state changes.</p>
<h3>DamageableObject.as</h3>
<pre>package
{
	public class DamageableObject extends MeshObject
	{
		protected var shields:int = 0;

		public function DamageableObject()
		{
			super();
		}

		protected function die():void
		{
			new EmitterContainer().startupEmitterContainer(engineManager, new Explosion(this.model.position));
			new SoundEffect().startupSoundEffect(this.engineManager, ResourceManager.ExplosionFX);
			this.shutdown();
		}

		public function damage(amount:int):void
		{
			this.shields -= amount;
			if (this.shields &lt;= 0)
				this.die();
		}
	}
}
</pre>
<p>We have already seen how the SoundEffect class is used in the WeaponManger. In addition we will add a sound effect when the player or an enemy dies. Because the DamageableObject class defines the die function that is called when an enemy or player dies, this is a logical place to add the sound effect.</p>
<p>We have made quite a few changes to accommodate the sound effects. Some of these changes were needed to fix up some shortsighted code design (it&#8217;s a rule I keep catching myself out with, but don&#8217;t take shortcuts), and some were needed to implement a more extensible way to define and create the weapons. However these changes did give us a clear place to create the sound effects, and will lead onto the topic of the next article: powerups.</p><p>The post <a href="https://www.thetechlabs.com/3d/away3d-shootemup-tutorial-sound-effects/">Away3D Shoot’em’Up Tutorial – Sound Effects</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.thetechlabs.com/3d/away3d-shootemup-tutorial-sound-effects/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Create from Scratch a Away3D Shoot’em’Up Game: Part 4</title>
		<link>https://www.thetechlabs.com/3d/create-from-scratch-a-away3d-shootemup-game-part-4/</link>
					<comments>https://www.thetechlabs.com/3d/create-from-scratch-a-away3d-shootemup-game-part-4/#comments</comments>
		
		<dc:creator><![CDATA[Matthew Casperson]]></dc:creator>
		<pubDate>Tue, 28 Jul 2009 12:00:15 +0000</pubDate>
				<category><![CDATA[3D]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Games]]></category>
		<category><![CDATA[Latest]]></category>
		<category><![CDATA[Tech Tutorials]]></category>
		<category><![CDATA[frameworks]]></category>
		<category><![CDATA[oop]]></category>
		<category><![CDATA[web-development]]></category>
		<guid isPermaLink="false">https://www.thetechlabs.com/?p=1509</guid>

					<description><![CDATA[<p>Now that we have the player and some enemies we need for them to be able to interact. This is done through collision detection which you will learn in this fourth part.</p>
<p>The post <a href="https://www.thetechlabs.com/3d/create-from-scratch-a-away3d-shootemup-game-part-4/">Create from Scratch a Away3D Shoot’em’Up Game: Part 4</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></description>
										<content:encoded><![CDATA[<p style="text-align: justify;">Now that we have the player and some enemies we need for them to be able to interact. This is done through collision detection which you will learn in this fourth part.</p>
<h3>Result</h3>
<p style="text-align: center;"><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/collisions.swf" type="application/x-shockwave-flash" width="545" height="400"><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/collisions.swf" /></object></p>
<p style="text-align: center;"><a title="Download Source Files" href="https://www.thetechlabs.com/wp-content/uploads/2009/07/CollisionDetectionSource.zip"><br />
<img loading="lazy" decoding="async" class="size-full wp-image-466 aligncenter" style="border: 0pt none;" title="Download Source Files" src="https://www.thetechlabs.com/wp-content/uploads/2009/04/sourcefiles.jpg" alt="Download Source Files" width="246" height="93" /></a></p>
<h3>Requirements</h3>
<p><a href="http://www.adobe.com/products/flex/?promoid=BPDEQ" target="_blank" rel="nofollow">Adobe Flex v3.02</a></p>
<p><a href="http://away3d.com/downloads" target="_blank" rel="nofollow">Away3D v3.3.3 source code</a></p>
<h3>Pre-Requisites</h3>
<p>You should read the previous articles in this series.</p>
<h3>Adding collisions</h3>
<p style="text-align: justify;">Last time we saw how to add a player and some enemies to the level. While they could both move around they did not interact with each other as you would expect; namely by crashing. This is because the neither the player nor the enemies had any idea where the other was. In order for the ships to start colliding we need a way to define the space that they are currently taking up. This is done in the MeshObject class.</p>
<h3>MeshObject.as</h3>
<pre>package
{
	import away3d.core.base.Object3D;

	internal class MeshObject extends BaseObject
	{
		public var model:Object3D = null;
		public var collisionName:String = CollisionIdentifiers.NONE;

		public function MeshObject()
		{
			super();
		}

		public function startupMeshObject(engineManager:EngineManager, model:Object3D):MeshObject
		{
			super.startupBaseObject(engineManager);
			this.model = model;
			this.engineManager.view.scene.addChild(model);
			return this;
		}

		public override function shutdown():void
		{
			if (model != null)
				engineManager.view.scene.removeChild(model);
			model = null;
			super.shutdown();
		}

		public function intersects(other:MeshObject):Boolean
		{
			// Use up to 6 separating planes
			if ( this.model.maxX + this.model.x &lt; other.model.minX + other.model.x )
				return false;
			if ( this.model.maxY + this.model.y &lt; other.model.minY + other.model.y )
 				return false;
			if ( this.model.maxZ + this.model.z &lt; other.model.minZ + other.model.z )
				return false;

   			if ( this.model.minX + this.model.x &gt; other.model.maxX + other.model.x )
   				return false;
   			if ( this.model.minY + this.model.y &gt; other.model.maxY + other.model.y )
   				return false;
   			if ( this.model.minZ + this.model.z &gt; other.model.maxZ + other.model.z)
				return false;

			// otherwise, must be intersecting
			return true;
		}
	}
}</pre>
<p style="text-align: justify;">We have added a property called collisionName, which identifies the type of object this is to the collision system (which will be implemented in the EngineManager class later on). This will allow us to specify which objects should collide &#8211; like players with enemies, enemies with the players bullets, the player with enemy bullets etc.</p>
<p style="text-align: justify;">There is also a new function called intersects, which is used to find out of two MeshObjects are currently colliding. It does this by using the bounding box defined by the minX, maxX, minY, maxY, minZ and maxZ properties of the Away3D Object3D class. If the two objects are intersecting (or colliding) true is returned, otherwise false is returned.</p>
<h3>CollisionIdentifiers.as</h3>
<pre>package
{
	public class CollisionIdentifiers
	{
		public static const NONE:String = "None";
		public static const PLAYER:String = "Player";
		public static const PLAYERWEAPON:String = "PlayerWeapon";
		public static const ENEMYWEAPON:String = "EnemyWeapon";
		public static const ENEMY:String = "Enemy";
		public static const POWERUP:String = "Powerup";
	}
}</pre>
<p style="text-align: justify;">Even though the collisionName property of the MeshObject is a string, it is handy to define these values as constants so the Flex compiler can pick up any spelling mistakes, instead of having to track down a misspelled collisionName yourself.</p>
<h3>EngineManager.as</h3>
<pre>package
{
	import away3d.cameras.Camera3D;
	import away3d.containers.View3D;
	import away3d.core.math.Number3D;
	import away3d.core.render.Renderer;
	import away3d.events.*;

	import flash.display.*;
	import flash.events.*;
	import flash.utils.*;

	import mx.collections.*;
	import mx.core.*;

	public class EngineManager extends UIComponent
	{
		public static const version:String = "1.0.0";
		protected static const MEASURED_MIN_WIDTH:int = 25;
		protected static const MEASURED_MIN_HEIGHT:int = 25;
		protected static const MEASURED_WIDTH:int = 100;
		protected static const MEASURED_HEIGHT:int = 100;

		// Away3D view
		internal var view:View3D = null;
		// Away3D camera
		internal var cam:Camera3D = null;
		// a collection of the BaseObjects
		protected var baseObjects:ArrayCollection = new ArrayCollection();
		// a collection where new BaseObjects are placed, to avoid adding items
		// to baseObjects while in the baseObjects collection while it is in a loop
		protected var newBaseObjects:ArrayCollection = new ArrayCollection();
		// a collection where removed BaseObjects are placed, to avoid removing items
		// to baseObjects while in the baseObjects collection while it is in a loop
		protected var removedBaseObjects:ArrayCollection = new ArrayCollection();
		// the last frame time
		protected var lastFrame:Date;
		// the application manager
		protected var applicationManager:ApplicationManager = null;
		// true if we need to shutdown the engine during the next frame and drop back to the main menu
		protected var myNextStateChange:String = "";
		// collision mappings
		protected var collisionMap:Dictionary = new Dictionary();

		internal function get MyApplicationManager():ApplicationManager
		{
			return applicationManager;
		}

		public function EngineManager()
		{
			super();
			addEventListener(Event.REMOVED_FROM_STAGE, this.shutdown);
			addEventListener(Event.ADDED_TO_STAGE, this.initializeEngine);
		}

		override protected function measure():void
		{
			super.measure();

			// set a bunch of predefined sizes
			this.measuredMinWidth = MEASURED_MIN_WIDTH;
			this.measuredMinHeight = MEASURED_MIN_HEIGHT;
			this.measuredHeight = MEASURED_HEIGHT;
			this.measuredWidth = MEASURED_WIDTH;
		}

		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			super.updateDisplayList(unscaledWidth, unscaledHeight);

			if (view != null)
			{
				// place the viewport in the middle of the control
				if(this.width / 2 != this.view.x)
					this.view.x = this.width / 2;
				if(this.height / 2 != this.view.y)
					this.view.y = this.height / 2;
			}
		}

		public function set nextStateChange(value:String):void
		{
			myNextStateChange = value;
		}

		protected function initializeEngine(event:Event):void
		{
			if (!view)
			{
				myNextStateChange = "";

				cam = new Camera3D({x:0, y:0, z:-100, lookat:new Number3D(0, 0, 0)});

				view = new View3D({x:stage.width/2, y:stage.height/2, camera:cam});
				view.renderer = Renderer.BASIC;
				view.mouseEnabled = true;
				addChild(view);

				addEventListener(Event.ENTER_FRAME, enterFrame);
				addEventListener(MouseEvent.MOUSE_DOWN, this.mouseDown);
				addEventListener(MouseEvent.MOUSE_UP, this.mouseUp);
				addEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
				addEventListener(MouseEvent.CLICK, this.click);

				// set the initial frame time
				lastFrame = new Date();

				// start the application manager
				applicationManager = new ApplicationManager().startupApplicationManager(this);
			}
		}

		protected function shutdown(event:Event):void
		{
			if (view)
			{
				applicationManager.shutdown();

				shutdownAll();

				removeChild(view);

				removeEventListener(Event.ENTER_FRAME, enterFrame);
				removeEventListener(MouseEvent.MOUSE_DOWN, this.mouseDown);
				removeEventListener(MouseEvent.MOUSE_UP, this.mouseUp);
				removeEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
				removeEventListener(MouseEvent.CLICK, this.click);

				applicationManager = null;
				view = null;
				cam = null;
				myNextStateChange = "";
			}
		}

		protected function enterFrame(event:Event):void
		{
			if (view != null)
			{
				// Calculate the time since the last frame
				var thisFrame:Date = new Date();
				var seconds:Number = (thisFrame.getTime() - lastFrame.getTime())/1000.0;
		    	lastFrame = thisFrame;

		    	// sync the baseObjects collection with any BaseObjects created or removed during the
		    	// render loop
		    	removeDeletedBaseObjects();
		    	insertNewBaseObjects();

		    	// allow each BaseObject to update itself
		    	for each (var baseObject:BaseObject in baseObjects)
		    		if (baseObject.inuse)
		    			baseObject.enterFrame(seconds);		  

		    	checkCollisions();  			

		    	// render the 3D scene
		    	view.render();
	  		}

	  		// set the currentState to MainMenu, which drops us back to the menu
	  		if (myNextStateChange != "")
	  		{
	  			Application.application.currentState = myNextStateChange;
	  			myNextStateChange = "";
	  		}
		}

		public function addBaseObject(baseObject:BaseObject):void
		{
			newBaseObjects.addItem(baseObject);
		}

		public function removeBaseObject(baseObject:BaseObject):void
		{
			removedBaseObjects.addItem(baseObject);
		}

		protected function insertNewBaseObjects():void
		{
			for each (var baseObject:BaseObject in newBaseObjects)
				baseObjects.addItem(baseObject);

			newBaseObjects.removeAll();
		}

		protected function removeDeletedBaseObjects():void
		{
			for each (var removedObject:BaseObject in removedBaseObjects)
			{
				var i:int = 0;
				for (i = 0; i &lt; baseObjects.length; ++i)
				{
					if (baseObjects.getItemAt(i) == removedObject)
					{
						baseObjects.removeItemAt(i);
						break;
					}
				}

			}

			removedBaseObjects.removeAll();
		}

		public function shutdownAll():void
		{
			// don't dispose objects twice
			for each (var baseObject:BaseObject in baseObjects)
			{
				var found:Boolean = false;
				for each (var removedObject:BaseObject in removedBaseObjects)
				{
					if (removedObject == baseObject)
					{
						found = true;
						break;
					}
				}

				if (!found)
					baseObject.shutdown();
			}

			removeDeletedBaseObjects();
		}

		public function click(event:MouseEvent):void
		{
			for each (var gameObject:BaseObject in baseObjects)
				if (gameObject.inuse)
					gameObject.click(event);
		}

		public function mouseDown(event:MouseEvent):void
		{
			for each (var gameObject:BaseObject in baseObjects)
				if (gameObject.inuse)
					gameObject.mouseDown(event);
		}

		public function mouseUp(event:MouseEvent):void
		{
			for each (var gameObject:BaseObject in baseObjects)
				if (gameObject.inuse)
					gameObject.mouseUp(event);
		}

		public function mouseMove(event:MouseEvent):void
		{
			for each (var gameObject:BaseObject in baseObjects)
				if (gameObject.inuse)
					gameObject.mouseMove(event);
		}

		public function addCollidingPair(collider1:String, collider2:String):void
		{
			if (collisionMap[collider1] == null)
				collisionMap[collider1] = new Array();

			if (collisionMap[collider2] == null)
				collisionMap[collider2] = new Array();

			if ((collisionMap[collider1] as Array).indexOf(collider2) == -1)
				collisionMap[collider1].push(collider2);
			if ((collisionMap[collider2] as Array).indexOf(collider1) == -1)
				collisionMap[collider2].push(collider1);
		}

		protected function checkCollisions():void
		{
	    	for (var i:int = 0; i &lt; baseObjects.length; ++i)
			{
				var gameObjectI:MeshObject = baseObjects.getItemAt(i) as MeshObject;

				if (gameObjectI)
				{
					for (var j:int = i + 1; j &lt; baseObjects.length; ++j)
					{
						var gameObjectJ:MeshObject = baseObjects.getItemAt(j) as MeshObject;

						if (gameObjectJ)
						{
							// early out for non-colliders
							var collisionNameNotNothing:Boolean = gameObjectI.collisionName != CollisionIdentifiers.NONE;
							// objects can still exist in the baseObjects collection after being disposed, so check
							var bothInUse:Boolean = gameObjectI.inuse &amp;&amp; gameObjectJ.inuse;
							// make sure we have an entry in the collisionMap
							var collisionMapEntryExists:Boolean = collisionMap[gameObjectI.collisionName] != null;
							// make sure the two objects are set to collide
							var testForCollision:Boolean = collisionMapEntryExists &amp;&amp; collisionMap[gameObjectI.collisionName].indexOf(gameObjectJ.collisionName) != -1

							if ( collisionNameNotNothing &amp;&amp;
								 bothInUse &amp;&amp;
								 collisionMapEntryExists &amp;&amp;
								 testForCollision)
							{
								if (gameObjectI.intersects(gameObjectJ))
								{
									gameObjectI.collision(gameObjectJ);
									gameObjectJ.collision(gameObjectI);
								}
							}
						}
					}
				}
			}
		}
	}
}</pre>
<p style="text-align: justify;">The actual detection of the collisions is done by the EngineManager.</p>
<p style="text-align: justify;">The addCollidingPair function maps the collisionName properties of the MeshObject against each other. This mapping tells the EngineManager that these two types of MeshObjects should be tested for collisions against each other.</p>
<p style="text-align: justify;">The checkCollisions function is where the collisions are actually determined and any colliding MeshObjects notified via their collision function.</p>
<pre>for (var i:int = 0; i &lt; baseObjects.length; ++i)
{
	var gameObjectI:MeshObject = baseObjects.getItemAt(i) as MeshObject;

	if (gameObjectI)
	{
		for (var j:int = i + 1; j &lt; baseObjects.length; ++j)
		{
			var gameObjectJ:MeshObject = baseObjects.getItemAt(j) as MeshObject;

			if (gameObjectJ)
			{
			...
			}
		}
	}
}</pre>
<p style="text-align: justify;">The baseObjects collection is looped through twice in a way that ensures that every MeshObject is tested against every other MeshObject once.</p>
<pre>// early out for non-colliders
var collisionNameNotNothing:Boolean = gameObjectI.collisionName != CollisionIdentifiers.NONE;
// objects can still exist in the baseObjects collection after being disposed, so check
var bothInUse:Boolean = gameObjectI.inuse &amp;&amp; gameObjectJ.inuse;
// make sure we have an entry in the collisionMap
var collisionMapEntryExists:Boolean = collisionMap[gameObjectI.collisionName] != null;
// make sure the two objects are set to collide
var testForCollision:Boolean = collisionMapEntryExists &amp;&amp; collisionMap[gameObjectI.collisionName].indexOf(gameObjectJ.collisionName) != -1</pre>
<p style="text-align: justify;">Next we need to make sure that the two MeshObjects we are looking at should be able to collide with each other. This is where the mappings set up in the addCollidingPair function are used.</p>
<pre>if ( collisionNameNotNothing &amp;&amp;
	 bothInUse &amp;&amp;
	 collisionMapEntryExists &amp;&amp;
	 testForCollision)
{
	if (gameObjectI.intersects(gameObjectJ))
	{
		gameObjectI.collision(gameObjectJ);
		gameObjectJ.collision(gameObjectI);
	}
}</pre>
<p style="text-align: justify;">Assuming all the necessary conditions have been met, the intersects method is used to determine if the two MeshObjects are actually colliding. If so they are both notified through their collision function.</p>
<h3>ApplicationManager.as</h3>
<pre>package
{
	import away3d.primitives.Cube;

	import mx.core.Application;

	public class ApplicationManager extends BaseObject
	{
		protected static const TIME_BETWEEN_BUILDINGS:Number = 2;
		protected static const TIME_BETWEEN_ENEMIES:Number = 1;
		protected static const TIME_TO_LEVEL_END:Number = 1;
		protected var timeToEndGame:Number = TIME_TO_LEVEL_END;
		protected var timeToNextBuilding:Number = 0;
		protected var timeToNextEnemy:Number = TIME_BETWEEN_ENEMIES;
		protected var levelHasEnded:Boolean = false;

		public function ApplicationManager()
		{
			super();
		}

		public function startupApplicationManager(engineManager:EngineManager):ApplicationManager
		{
			this.startupBaseObject(engineManager);
			this.engineManager.addCollidingPair(CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYER);
			return this;
		}

		public function startLevel1():void
		{
			timeToNextBuilding = 0;

			new BackgroundPlane().startupBackgroundPlane(engineManager);
			new Player().startupPlayer(engineManager);

			// prepopulate the city
			for (var i:int = 0; i &lt; 5; ++i)
			{
				var building:BackgroundBuilding = new BackgroundBuilding();
				building.startupBackgroundBuilding(engineManager);
				building.enterFrame((i + 1) * 2);
			}
		}

		public function levelEnded():void
		{
			levelHasEnded = true;
		}

		public override function shutdown():void
		{
			super.shutdown();
		}

		public override function enterFrame(dt:Number):void
		{
			timeToNextBuilding -= dt;
			if (timeToNextBuilding &lt;= 0)
			{
				timeToNextBuilding = TIME_BETWEEN_BUILDINGS;
				new BackgroundBuilding().startupBackgroundBuilding(engineManager);
			}

			timeToNextEnemy -= dt;
			if (timeToNextEnemy &lt;= 0)
			{
				timeToNextEnemy = TIME_BETWEEN_ENEMIES;
				new Enemy().startupBasicEnemy(engineManager);
			}

			if (levelHasEnded)
			{
				timeToEndGame -= dt;
				if (timeToEndGame &lt;= 0)
				{
					engineManager.nextStateChange = "MainMenu";
				}
			}
		}
	}
}</pre>
<p style="text-align: justify;">The ApplicationManager is used to specify which MeshObjects should collide with which.</p>
<pre>this.engineManager.addCollidingPair(CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYER);</pre>
<p style="text-align: justify;">Here, in the startupApplicationmanager function, a call to the EngineManagers addCollidingPair function is made specifying that enemies and players should collide with each other.</p>
<pre>if (levelHasEnded)
{
	timeToEndGame -= dt;
	if (timeToEndGame &lt;= 0)
	{
		engineManager.nextStateChange = "MainMenu";
	}
}</pre>
<p style="text-align: justify;">We also want to be able to drop back to the main menu once the player has died, but we don&#8217;t want this to happen instantly. So instead we define a new proeprty called levelHasEnded, which will be set to true by the player dies via the levelEnded function. When the player has signalled that the level should end the timeToEndGame property starts counting down, and after a period of time the EngineManagers nextStateChange property is set the &#8216;MainMenu&#8217; to indicate that on the next frame the state should be changed.</p>
<h3>Enemy.as</h3>
<pre>package
{
	import away3d.materials.BitmapMaterial;
	import away3d.primitives.Plane;

	public class Enemy extends MeshObject
	{
		protected static const ENEMY_X_LIMIT:Number = 70;
		protected static const ENEMY_Y_LIMIT:Number = 60;
		protected static const ENEMY_X_START:Number = 65;
		protected static const ENEMY_Y_START:Number = 55;
		protected static const ENEMY_WIDTH:Number = 16;
		protected static const ENEMY_HEIGHT:Number = 5;
		protected static const BASIC_ENEMY_SPEED:Number = 50;
		protected var logic:Function = null;

		public function Enemy()
		{
			super();
		}

		public override function shutdown():void
		{
			super.shutdown();
			this.logic = null;
		}

		protected function startupEnemy(engineManager:EngineManager, material:BitmapMaterial):Enemy
		{
			var plane:Plane = new Plane(
				{material:material,
				width:ENEMY_WIDTH,
				height:ENEMY_HEIGHT,
				yUp:false});
			super.startupMeshObject(engineManager, plane);

			this.collisionName = CollisionIdentifiers.ENEMY;

			return this;
		}

		public function startupBasicEnemy(engineManager:EngineManager):Enemy
		{
			this.startupEnemy(engineManager, ResourceManager.Enemy_Tex);
			this.logic = basicEnemyLogic;
			this.model.x = ENEMY_X_LIMIT;
			this.model.y = MathUtils.randRange(-ENEMY_Y_START, ENEMY_Y_START);
			return this;
		}

		public override function enterFrame(dt:Number):void
		{
			if (logic != null)
				logic(dt);

			if (this.model.x &gt; ENEMY_X_LIMIT ||
				this.model.x &lt; -ENEMY_X_LIMIT ||
				this.model.y &gt; ENEMY_Y_LIMIT ||
				this.model.y &lt; -ENEMY_Y_LIMIT)
				this.shutdown();
		}

		protected function basicEnemyLogic(dt:Number):void
		{
			this.model.x -= BASIC_ENEMY_SPEED * dt;
		}

		public override function collision(other:BaseObject):void
		{
			this.shutdown();
		}
	}
}</pre>
<p style="text-align: justify;">We only need to make two changes to the Enemy class.</p>
<pre>this.collisionName = CollisionIdentifiers.ENEMY;</pre>
<p style="text-align: justify;">The first is to specify the collision name, which identifies this MeshObject as an enemy to the collision system.</p>
<pre>public override function collision(other:BaseObject):void
{
	this.shutdown();
}</pre>
<p style="text-align: justify;">The second is to override the collision function, and call the shutdown function when a collision has been detected.</p>
<h3>Player.as</h3>
<pre>package
{
	import away3d.core.math.Number3D;
	import away3d.events.MouseEvent3D;
	import away3d.primitives.*;

	import flash.events.*;
	import flash.geom.*;
	import flash.media.*;

	import mx.core.*;

	public class Player extends MeshObject
	{
		protected static const PLAYER_X_LIMIT:Number = 52;
		protected static const PLAYER_Y_LIMIT:Number = 38;
		protected static const PLAYER_WIDTH:Number = 16;
		protected static const PLAYER_HEIGHT:Number = 5;
		protected static const COLLISION_PLANE_WIDTH:Number = 150;
		protected static const COLLISION_PLANE_HEIGHT:Number = 100;
		protected var collisionPlane:MeshObject = null;

		public function Player()
		{
			super();
		}

		public function startupPlayer(engineManager:EngineManager):Player
		{
			var plane:Plane = new Plane(
				{material:ResourceManager.Player_Tex,
				width:PLAYER_WIDTH,
				height:PLAYER_HEIGHT,
				yUp:false});
			super.startupMeshObject(engineManager, plane);

			var collisionPlaneMesh:Plane = new Plane(
				{material:new NullMaterial(),
				width:COLLISION_PLANE_WIDTH,
				height:COLLISION_PLANE_HEIGHT,
				yUp:false});
			collisionPlane = new MeshObject().startupMeshObject(engineManager, collisionPlaneMesh);
			collisionPlaneMesh.addEventListener(MouseEvent3D.MOUSE_MOVE, this.mouseMove3D);

			this.collisionName = CollisionIdentifiers.PLAYER;

			return this;
		}

		override public function shutdown():void
		{
			collisionPlane.model.removeEventListener(MouseEvent3D.MOUSE_MOVE, this.mouseMove3D);
			collisionPlane.shutdown();
			collisionPlane = null;
			super.shutdown();
		}

		override public function enterFrame(dt:Number):void
		{
			super.enterFrame(dt);

			if (this.model.x &gt; PLAYER_X_LIMIT)
				this.model.x = PLAYER_X_LIMIT;
			if (this.model.x &lt; -PLAYER_X_LIMIT)
				this.model.x = -PLAYER_X_LIMIT;

			if (this.model.y &gt; PLAYER_Y_LIMIT)
				this.model.y = PLAYER_Y_LIMIT;
			if (this.model.y &lt; -PLAYER_Y_LIMIT)
				this.model.y = -PLAYER_Y_LIMIT;
		}

		public function mouseMove3D(event:MouseEvent3D):void
		{
			if (this.model)
			{
				this.model.x = event.sceneX;
				this.model.y = event.sceneY;
			}
		}

		public override function collision(other:BaseObject):void
		{
			engineManager.MyApplicationManager.levelEnded();
			this.shutdown();
		}
	}
}</pre>
<p style="text-align: justify;">The same two changes are made to the Player class. However, before the shutdown function is called (in the collision function), we first notify the Applicationmanager that the current level should end by calling the levelEnded function.</p>
<h3>Collisions.mxml</h3>
<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;mx:Application
 xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="absolute"
 xmlns:ns1="*"
 width="600"
 height="400"
 currentState="MainMenu"
 frameRate="100"
 backgroundGradientAlphas="[1.0, 1.0]"
 backgroundGradientColors="[#040522, #162654]"&gt;

	&lt;mx:states&gt;
		&lt;mx:State name="MainMenu"&gt;
			&lt;mx:AddChild position="lastChild"&gt;
				&lt;mx:Button x="10" y="368" label="Start" id="btnStart" click="{Application.application.currentState = 'Game';}"/&gt;
			&lt;/mx:AddChild&gt;
			&lt;mx:AddChild position="lastChild"&gt;
				&lt;mx:Image x="281" y="10" source="@Embed(source='../media/frontscreen.png')"/&gt;
			&lt;/mx:AddChild&gt;
			&lt;mx:AddChild position="lastChild"&gt;
				&lt;mx:Image x="465" y="265" source="@Embed(source='../media/thetechlabs_logosmall.png')"/&gt;
			&lt;/mx:AddChild&gt;
			&lt;mx:AddChild position="lastChild"&gt;
				&lt;mx:Text x="10" y="10" text="Away3D Shoot'em'Up tutorial" color="#FFFFFF" fontSize="16"/&gt;
			&lt;/mx:AddChild&gt;
			&lt;mx:AddChild position="lastChild"&gt;
				&lt;mx:Text x="10" y="42" text="Brought to you by The Tech Labs" color="#FFFFFF" fontSize="16"/&gt;
			&lt;/mx:AddChild&gt;
			&lt;mx:AddChild position="lastChild"&gt;
				&lt;mx:Text x="10" y="74" text="Written by Matthew Casperson" color="#FFFFFF" fontSize="16"/&gt;
			&lt;/mx:AddChild&gt;
		&lt;/mx:State&gt;
		&lt;mx:State name="Game"&gt;
			&lt;mx:enterState&gt;
				&lt;![CDATA[
					engineManager.MyApplicationManager.startLevel1();
					Mouse.hide();
				]]&gt;
			&lt;/mx:enterState&gt;
			&lt;mx:exitState&gt;
				&lt;![CDATA[
					Mouse.show();
				]]&gt;
			&lt;/mx:exitState&gt;
			&lt;mx:AddChild&gt;
				&lt;ns1:EngineManager x="0" y="0" width="100%" height="100%" id="engineManager"/&gt;
			&lt;/mx:AddChild&gt;
		&lt;/mx:State&gt;
	&lt;/mx:states&gt;
&lt;/mx:Application&gt;</pre>
<p style="text-align: justify;">One final change has to be made to the MXML file. When the level is started (because the Game state has been activated) we want to hide the mouse cursor by calling Mouse.hide(). Then when the main menu is shown (because the MainMenu state has been activated) we want to show the mouse cursor by calling Mouse.show().</p>
<p style="text-align: justify;">With collision detection enabled we can detected when the player and an enemy collide, remove both from the game, and end the level (after a short delay). We can also use the same collision detection routines to implement weapons, which we will do in the next article.</p><p>The post <a href="https://www.thetechlabs.com/3d/create-from-scratch-a-away3d-shootemup-game-part-4/">Create from Scratch a Away3D Shoot’em’Up Game: Part 4</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.thetechlabs.com/3d/create-from-scratch-a-away3d-shootemup-game-part-4/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Getting Started With BetweenAS3</title>
		<link>https://www.thetechlabs.com/tech-tutorials/flash/getting-started-with-betweenas3/</link>
					<comments>https://www.thetechlabs.com/tech-tutorials/flash/getting-started-with-betweenas3/#comments</comments>
		
		<dc:creator><![CDATA[Devon]]></dc:creator>
		<pubDate>Tue, 21 Jul 2009 12:00:44 +0000</pubDate>
				<category><![CDATA[Effects]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Latest]]></category>
		<category><![CDATA[Tech Tutorials]]></category>
		<category><![CDATA[as3]]></category>
		<category><![CDATA[betweeas3]]></category>
		<guid isPermaLink="false">https://www.thetechlabs.com/?p=1458</guid>

					<description><![CDATA[<p>There are many popular open source tweening engines in the Flash community today. Numbered among these are Tweener, TweenLite/Tweenmax, Tweensy, and GTween. This is a short look at the capabilities of newcomer to the scene: BetweenAS3. BetweenAS3 is the tweening engine brainchild of Yoshihiro (yossy) Shindo and is available as part of the Spark project (i.e. it is brought to us by one of the same group that brought the world the FLARToolkit for Augmented Reality in Flash and countless other goodies).</p>
<p>The post <a href="https://www.thetechlabs.com/tech-tutorials/flash/getting-started-with-betweenas3/">Getting Started With BetweenAS3</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></description>
										<content:encoded><![CDATA[<p style="text-align: justify;">There are many popular open source tweening engines in the Flash community today. Numbered among these are Tweener, TweenLite/Tweenmax, Tweensy, and GTween. This is a short look at the capabilities of newcomer to the scene: BetweenAS3. BetweenAS3 is the tweening engine brainchild of Yoshihiro (yossy) Shindo and is available as part of the Spark project (i.e. it is brought to us by one of the same group that brought the world the FLARToolkit for Augmented Reality in Flash and countless other goodies).</p>
<h2>Requirements</h2>
<p style="text-align: justify;">To go through the code examples presented here, you will, of course, need the BetweenAS3 library added to your class path. The library can be downloaded from the .svn depository <a href="http://www.libspark.org/svn/as3/BetweenAS3/trunk/" target="_blank">here</a>. You&#8217;ll notice that there are two branches to the library &#8211; one for Flash Player 9 and one for Flash Player 10. Although the syntax for the two branches is the same, I&#8217;ve used the Flash Player 10 branch for the examples here. I&#8217;ve also written the examples here as a pure Actionscript project, so something that can compile actionscript projects, such as FlashBuilder/Flexbuilder or FlashDevelop plus the Flex SDK is also required. Flash CS4 can also be used if each example class is used as a .fla document class. The included download contains all actionscript files plus the FlashDevelopment project file.</p>
<p style="text-align: center;"><a href="https://www.thetechlabs.com/wp-content/uploads/2009/07/downloads.zip"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-466" style="border: 0pt none;" title="Download Source Files" src="https://www.thetechlabs.com/wp-content/uploads/2009/04/sourcefiles.jpg" alt="Download Source Files" width="246" height="93" /></a></p>
<h2>Pre-Requisites</h2>
<p style="text-align: justify;">As the name BetweenAS3 implies, all code presented will be in Actionscript 3. While it would be helpful (and is assumed) that the reader has at least a basic working knowledge of Actionscript 3, this isn&#8217;t 100% necessary.</p>
<h2>Basic Syntax and Functionality</h2>
<p style="text-align: justify;">Primarily, BetweenAS3 relies on several static method calls. Each of these static methods will return an instance of an ITween interface. Because of this, BetweenAS3 offers a great deal of flexibility as each of these static methods may nest any number of additional methods thereby building extremely complex tweens from relatively simple syntax.</p>
<p style="text-align: justify;">In its most simple form, a basic BetweenAS3 tween may look like this:</p>
<pre style="text-align: justify;">var tween:ITween = BetweenAS3.tween(targetObject, toObject, fromObject, time, easingMethod);</pre>
<p style="text-align: justify;">Once created, the tween can be started using the play() method, stopped using the stop() method or controlled with the Movieclip like methods gotoAndPlay(time) or gotoAndStop(time). You can determine the length of the tween in seconds with the duration property, the current time of the tween with the position property and can determine whether or not the tween is playing at any given moment with the aptly named isPlaying property.</p>
<p style="text-align: justify;">Like all good tweening engines, with BetweenAS3 you are able to trigger actionscript methods when the tween starts, stops, updates or completes. Unlike most tweening engines, though, BetweenAS3 offers two ways of doing this. You can either use the standard AS3 event model and listen for a BetweenEvent, or you can use a more AS2 style approach and add callback methods to the tween instance. For example, say you wanted to call the method named tweenDone when a tween completes. You could say:</p>
<pre>tween.addEventListener(BetweenEvent.COMPLETE, tweenDone);

private function tweenDone(event:BetweenEvent):void {
	// the tween is complete
}</pre>
<p style="text-align: justify;">Or, instead, you could say:</p>
<pre>tween.onComplete = tweenDone;

private function tweenDone():void {
	// the tween is complete
}</pre>
<p style="text-align: justify;">Using this latter approach, it is also possible to pass parameters to the callback method by passing an array of parameters to the tween instance&#8217;s onCompleteParams property like so:</p>
<pre>tween.onComplete = tweenDone;
tween.onCompleteParams = ["hello", 3];

private function tweenDone(param1:String, param2:int):void {
	trace(param1, param2);
}</pre>
<p style="text-align: justify;">Now that we are armed with some basic knowledge of how BetweenAS3 operates, let&#8217;s take a look at some specific examples of this powerful tweening engine in action.</p>
<h2>The Basic Tween</h2>
<p style="text-align: justify;">Before diving straight into examples, let&#8217;s first create a couple simple class files that we can reuse throughout this tutorial. The first thing we&#8217;ll want is a target to tween. For this we&#8217;ll simply extend the Sprite object and use its graphics property to draw a small grey square. This then is our TweenTarget class:</p>
<pre>package  {

	import flash.display.Sprite;

	/**
	 * Simple DisplayObject to demonstrate BetweenAS3
	 */
	public class TweenTarget extends Sprite {

		public function TweenTarget() {
			graphics.beginFill(0x999999);
			graphics.drawRect(0, 0, 25, 25);
			graphics.endFill();
		}
	}
}</pre>
<p style="text-align: justify;">The second bit of code we&#8217;ll want to get out of the way is a template that we can use with each example. This class will just create an of a textfield (that simply says to click to start or stop the tween), and a tween instance class property. It will also contain a toggleTween method that, using the methods mentioned above, will allow our ITween instance to start or stop by clicking the stage. Once our template is made, the only things that will change from example to example are the name of the class (unless you&#8217;d like to overwrite each example), the initTween method which creates our class property tween instance, perhaps the easing method imports, and possibly a few other small details. The general template, then will look like this:</p>
<pre>package  {

	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;

	import org.libspark.betweenas3.BetweenAS3;
	import org.libspark.betweenas3.easing.Expo;
	import org.libspark.betweenas3.tweens.ITween;

	[SWF(width='540', height='200', backgroundColor='#000000', frameRate='60')]
	public class TweenExample extends Sprite {

		private var _tween:ITween;
		private var _tf:TextField;

		public function TweenExample() {
			addEventListener(Event.ADDED_TO_STAGE, init);
		}

		private function init(event:Event):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);

			initText();
			initTween();

			stage.addEventListener(MouseEvent.CLICK, toggleTween);
		}

		private function initText():void {
			var fmt:TextFormat = new TextFormat("_sans", 12, 0xFFFFFF);
			_tf = new TextField();
			_tf.selectable = false;
			_tf.mouseEnabled = false;
			_tf.autoSize = TextFieldAutoSize.LEFT;
			_tf.defaultTextFormat = fmt;
			_tf.text = "Click to start/stop tween.";
			_tf.x = Math.round(stage.stageWidth * .5 - _tf.width * .5);
			_tf.y = Math.round(stage.stageHeight - _tf.height - 10);
			addChild(_tf);
		}

		private function initTween():void {
			// will change from example to example
		}

		private function toggleTween(event:MouseEvent):void {
			// if the tween is playing, stop it
			if (_tween.isPlaying) {
				_tween.stop();
			} else {
				// otherwise if the tween is at the end, start it over
				if (_tween.position == _tween.duration) {
					_tween.gotoAndPlay(0);
				// if it's not at the end, simply play the tween
				} else {
					_tween.play();
				}
			}
		}
	}
}</pre>
<p style="text-align: justify;">Now that we are armed with these basic items, let&#8217;s roll up our sleeves and dig into a concrete example of BetweenAS3 in action. As mentioned earlier, the syntax for creating a basic tween looks like this:</p>
<pre>_tween = BetweenAS3.tween(target, toObject, fromObject, time, easing);</pre>
<p style="text-align: justify;">In more detail, the target is the obejct whose property or properties we wish to tween, the toObject is an object containing properties and their corresponding ending values. Likewise, the fromObject is an object containing the same properties as the toObject, but with their beginning value. Time is the duration of the tween in seconds, and easing is an IEasing interface instance which we can get from the org.libspark.betweenas3.easing package. In our initTween method, then, let&#8217;s create a TweenTarget instance, add it to the display list and use it as the target of our tween instance. So initTween becomes:</p>
<pre>private function initTween():void {
	var targ:TweenTarget = new TweenTarget();
	targ.x = 20;
	targ.y = 60;
	addChild(targ);
	_tween = BetweenAS3.tween(targ, { x:500 }, null, 2.0, Expo.easeOut);
}</pre>
<p style="text-align: justify;">Notice that we passed a null in place of a fromObject. By doing so, we are telling BetweenAS3 to use the tween target&#8217;s current properties. We already created the toggleTween method which will play the tween when you click the stage, so go ahead and compile the .swf. You should get something like the example below:</p>
<p><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/basictween.swf" type="application/x-shockwave-flash" width="540" height="200"><param name="quality" value="high" /><param name="id" value="basictween" /><param name="name" value="basictween" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/basictween.swf" /><param name="bgcolor" value="#000000" /></object></p>
<h2>Delaying a Tween</h2>
<p style="text-align: justify;">As mentioned, complex tweens can be built with BetweenAS3 by nesting various static methods of the class. So far we have looked at only one of those methods, tween. Let&#8217;s look at another, now, delay. Like the tween method, delay returns an instance of an ITween interface. The delay method syntax looks like this:</p>
<pre>var tween:ITween = BetweenAS3.delay(ITweenToDelay, delayTime, postDelayTime);</pre>
<p style="text-align: justify;">Here, the ITweenToDelay is a tween instance we wish to delay, the delay time is how many seconds the tween should be delayed before the tween starts and the postDelayTime is how many seconds the tween should be delayed before the next tween starts. The postDelayTime defaults to 0.0 and, in this example, we will leave it so.</p>
<p style="text-align: justify;">So, let&#8217;s go back to our TweenExample template and change the initTween method to look like this:</p>
<pre>private function initTween():void {
	var targ:TweenTarget = new TweenTarget();
	targ.x = 20;
	targ.y = 60;
	addChild(targ);
	var t:ITween = BetweenAS3.tween(targ, { x:500 }, null, 2.0, Expo.easeOut);
	_tween = BetweenAS3.delay(t, 1.0);
}</pre>
<p style="text-align: justify;">Now, when we compile our example, we see that when we click on the stage, there is a one second delay before the tween target tweens its way across the stage like below.</p>
<p><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/delaytween.swf" type="application/x-shockwave-flash" width="540" height="200"><param name="quality" value="high" /><param name="id" value="delaytween" /><param name="name" value="delaytween" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/delaytween.swf" /><param name="bgcolor" value="#000000" /></object></p>
<h2>Parallel and Serial Tweens</h2>
<p style="text-align: justify;">The next static methods of the BetweenAS3 class we&#8217;ll consider are parallel and serial. The parallel method lets you run any number of tween instances at the same time. Similarly, the serial method allows you to run any number of tween instances in a row (one after another). Syntactically, both methods are identical, you simply pass any number of ITween instances as parameters.</p>
<p style="text-align: justify;">For our parallel example, let&#8217;s change our initTween method to look like this:</p>
<pre>private function initTween():void {
	var targ1:TweenTarget = new TweenTarget();
	targ1.x = 20;
	targ1.y = 25;
	addChild(targ1);

	var targ2:TweenTarget = new TweenTarget();
	targ2.x = 20;
	targ2.y = 60;
	addChild(targ2);

	var t1:ITween = BetweenAS3.tween(targ1, { x:500 }, null, 2.0, Expo.easeOut);
	var t2:ITween = BetweenAS3.tween(targ2, { x:500 }, null, 2.0, Expo.easeOut);
	_tween = BetweenAS3.parallel(t1, t2);
}</pre>
<p style="text-align: justify;">Once compiled, that will look like this:</p>
<p><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/paralleltween.swf" type="application/x-shockwave-flash" width="540" height="200"><param name="quality" value="high" /><param name="id" value="paralleltween" /><param name="name" value="paralleltween" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/paralleltween.swf" /><param name="bgcolor" value="#000000" /></object></p>
<p style="text-align: justify;">And for our serial example, try this for the initTween method:</p>
<pre>private function initTween():void {
	var targ1:TweenTarget = new TweenTarget();
	targ1.x = 20;
	targ1.y = 25;
	addChild(targ1);

	var targ2:TweenTarget = new TweenTarget();
	targ2.x = 20;
	targ2.y = 60;
	addChild(targ2);

	var t1:ITween = BetweenAS3.tween(targ1, { x:500 }, null, 2.0, Elastic.easeOut);
	var t2:ITween = BetweenAS3.tween(targ2, { x:500 }, null, 2.0, Elastic.easeOut);
	_tween = BetweenAS3.serial(t1, t2);
}</pre>
<p style="text-align: justify;">Note that we&#8217;ve tried a different easing method here which means a different easing method will need to be imported from the org.libspark.betweenas3.easing package before compiling. Once compiled, though, you&#8217;ll see something like this:</p>
<p><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/serialtween.swf" type="application/x-shockwave-flash" width="540" height="200"><param name="quality" value="high" /><param name="id" value="serialtween" /><param name="name" value="serialtween" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/serialtween.swf" /><param name="bgcolor" value="#000000" /></object></p>
<p style="text-align: justify;">At this point it should start to be obvious what was meant when it was stated that very complex tweens could be built up by nesting simpler tweens. For example, you could easily pass two parallel tweens and a delayed tween to the BetweenAS3 serial method and so on.</p>
<h2>Reversing and Repeating Tweens</h2>
<p style="text-align: justify;">The next two static methods we&#8217;ll look at are reverse and repeat. The reverse method, as its name suggests, reverses a tween and takes two parameters, the tween to reverse and a boolean indicating whether or not to reverse the tween&#8217;s position. Most often you would simply leave this boolean set to its default state of true. The repeat method, true to it&#8217;s name, repeats a specified tween. Repeat accepts two arguments: the tween instance to repeat and the number of times it should repeat.</p>
<p style="text-align: justify;">This time our initTween method will be a little bit trickier and we&#8217;ll really start to see the power of nesting BetweenAS3 calls. We&#8217;ll begin by creating a basic tween that moves our TweenTarget instance across the stage with a nice elasticity. Nothing new there. Then, though, we&#8217;ll create a repeated tween that repeats four times. That repeated tween will be passed an instance of a serial tween that tweens first the basic tween then the reverse of the basic tween. Our initTween method then will look like this:</p>
<pre>private function initTween():void {
	var targ1:TweenTarget = new TweenTarget();
	targ1.x = 20;
	targ1.y = 60;
	addChild(targ1);
	var t1:ITween = BetweenAS3.tween(targ1, { x:500 }, { x:targ1.x }, 2.0, Elastic.easeInOut);
	_tween = BetweenAS3.repeat(BetweenAS3.serial(t1, BetweenAS3.reverse(t1)), 4);
}</pre>
<p style="text-align: justify;">Compiled, you should get a result such as this:</p>
<p><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/reversetween.swf" type="application/x-shockwave-flash" width="540" height="200"><param name="quality" value="high" /><param name="id" value="reversetween" /><param name="name" value="reversetween" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/reversetween.swf" /><param name="bgcolor" value="#000000" /></object></p>
<h2>Adding and Removing Children</h2>
<p style="text-align: justify;">One of the interesting benefits of using BetweenAS3 is the ability to add and remove child display objects as a tween using the static methods addChild and removeFromParent respectively. The addChild method expects two arguments: the child to add and the parent the child should be added to. The removeFromParent accepts only a single argument, the child to remove. Both of these methods, again, return ITween instances and can be nested into other BetweenAS3 methods to build up complex tween sequences.</p>
<p style="text-align: justify;">In this example, we&#8217;ll instantiate three TweenTarget instances, use the BetweenAS3.addChild method to add them to the display list with a .5 second delay, tween them all across the stage in parallel, then use the removeFromParent method to get remove them from the display list. This time our initTween method looks like this:</p>
<pre>private function initTween():void {
	var targ1:TweenTarget = new TweenTarget();
	targ1.x = 20;
	targ1.y = 25;

	var targ2:TweenTarget = new TweenTarget();
	targ2.x = 20;
	targ2.y = 60;

	var targ3:TweenTarget = new TweenTarget();
	targ3.x = 20;
	targ3.y = 95;

	var t1:ITween = BetweenAS3.delay(BetweenAS3.addChild(targ1, this), .5);
	var t2:ITween = BetweenAS3.delay(BetweenAS3.addChild(targ2, this), .5);
	var t3:ITween = BetweenAS3.delay(BetweenAS3.addChild(targ3, this), .5, .5);
	var t4:ITween = BetweenAS3.tween(targ1, { x:500 }, null, 2.0, Elastic.easeOut);
	var t5:ITween = BetweenAS3.tween(targ2, { x:500 }, null, 2.0, Elastic.easeIn);
	var t6:ITween = BetweenAS3.tween(targ3, { x:500 }, null, 2.0, Elastic.easeInOut);
	var t7:ITween = BetweenAS3.parallel(t4, t5, t6);
	var t8:ITween = BetweenAS3.delay(BetweenAS3.removeFromParent(targ1), .5);
	var t9:ITween = BetweenAS3.delay(BetweenAS3.removeFromParent(targ2), .5);
	var t10:ITween = BetweenAS3.delay(BetweenAS3.removeFromParent(targ3), .5);

	_tween = BetweenAS3.serial(t1, t2, t3, t7, t8, t9, t10);
}</pre>
<p style="text-align: justify;">Note that for the sake of legibility, I created several single ITween instances, but if you really wanted to, you could actually create the _tween instance in a single line like so:</p>
<pre>_tween = BetweenAS3.serial(BetweenAS3.delay(BetweenAS3.addChild(targ1, this), .5), BetweenAS3.delay(BetweenAS3.addChild(targ2, this), .5), BetweenAS3.delay(BetweenAS3.addChild(targ3, this), .5, .5), BetweenAS3.parallel(BetweenAS3.tween(targ1, { x:500 }, null, 2.0, Elastic.easeOut), BetweenAS3.tween(targ2, { x:500 }, null, 2.0, Elastic.easeIn), BetweenAS3.tween(targ3, { x:500 }, null, 2.0, Elastic.easeInOut)), BetweenAS3.delay(BetweenAS3.removeFromParent(targ1), .5), BetweenAS3.delay(BetweenAS3.removeFromParent(targ2), .5), BetweenAS3.delay(BetweenAS3.removeFromParent(targ3), .5));</pre>
<p style="text-align: justify;">Such is the power of nesting tween methods in BetweenAS3.</p>
<p style="text-align: justify;">Whichever method you choose to create your tween instance, the compiled .swf will look like the example below:</p>
<p><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/addchildtween.swf" type="application/x-shockwave-flash" width="540" height="200"><param name="quality" value="high" /><param name="id" value="addchildtween" /><param name="name" value="addchildtween" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/addchildtween.swf" /><param name="bgcolor" value="#000000" /></object></p>
<h2>Color Tweening</h2>
<p style="text-align: justify;">In addition to nesting ITween instances it is also possible with BetweenAS3 to nest property objects to build complex tweens. In this example we&#8217;ll take a look at how to tween the properties of the colorTransform property of the transform property of the TweenTarget instance. This time in our initTween method, we&#8217;ll enlarge our TweenTarget instance, then tween its color. Our initTween method will look like this:</p>
<pre>private function initTween():void {
	var targ:TweenTarget = new TweenTarget();
	targ.x = 235;
	targ.y = 40;
	targ.scaleX = 3;
	targ.scaleY = 3;
	addChild(targ);
	_tween = BetweenAS3.tween(targ, { transform:{colorTransform:{ redOffset:-255, greenOffset:-255, blueOffset:255 }} }, null, 4.0, Expo.easeOut);
}</pre>
<p style="text-align: justify;">Compiled, you should see something like the below:</p>
<p><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/colortween.swf" type="application/x-shockwave-flash" width="540" height="200"><param name="quality" value="high" /><param name="id" value="colortween" /><param name="name" value="colortween" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/colortween.swf" /><param name="bgcolor" value="#000000" /></object></p>
<h2>Filter Tweening</h2>
<p style="text-align: justify;">Again, by nesting property objects, BetweenAS3 can also be used to tween DisplayObject filters. In this quick example, we&#8217;ll take a look at tweening a glow filter back and forth. Before we begin though, there is one other property of the ITween interface to take a look at: the stopOnComplete property. This property is a boolean that tells the tween to stop when it is finished. If set to false, as it will be in this example, the tween will run indefinitely starting at it&#8217;s beginning each time it completes. In this example, we&#8217;ll again enlarge our TweenTarget instance, add a GlowFilter object to its filters property, then, finally tween that filter. Our initTween method will look like this:</p>
<pre>private function initTween():void {
	var targ:TweenTarget = new TweenTarget();
	targ.x = 235;
	targ.y = 40;
	targ.scaleX = 3;
	targ.scaleY = 3;
	addChild(targ);
	targ.filters = [new GlowFilter(0xFFFF00, 1, 0, 0, 2, 3)];
	var t1:ITween = BetweenAS3.tween(targ, { _glowFilter: { blurX:32, blurY:32 } }, { _glowFilter: { blurX:0, blurY:0 }},  1.0, Expo.easeOut);
	_tween = BetweenAS3.serial(t1, BetweenAS3.reverse(t1));
	_tween.stopOnComplete = false;
}</pre>
<p style="text-align: justify;">Compiled our glow filter example will look like this:</p>
<p><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/filtertween.swf" type="application/x-shockwave-flash" width="540" height="200"><param name="quality" value="high" /><param name="id" value="filtertween" /><param name="name" value="filtertween" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/filtertween.swf" /><param name="bgcolor" value="#000000" /></object></p>
<p style="text-align: justify;">In case you&#8217;re wondering where that &#8220;_glowFilter&#8221; property came from, BetweenAS3 will, by default, recognize the following filter properties: &#8220;_bevelFilter&#8221;, &#8220;_blurFilter&#8221;, &#8220;_colorMatrixFilter&#8221;, &#8220;_convolutionFilter&#8221;, &#8220;_displacementMapFilter&#8221;, &#8220;_dropShadowFilter&#8221;, &#8220;_glowFilter&#8221;, &#8220;_gradientBevelFilter&#8221;, &#8220;_gradientGlowFilter&#8221;, and &#8220;_shaderFilter&#8221;.</p>
<h2>Tweening Array Members</h2>
<p style="text-align: justify;">Possibly one of my favorite features of BetweenAS3 is its ability to tween the members of Array instances just like they were properties of an object (which, when you think about it, they are). Why would you want to tween array members, you ask. Well, one possible example is that you want to tween an image from &#8220;normal&#8221; to &#8220;embossed&#8221; and back again. You can easily emboss an image using a ConvolutionFilter, but to tween the transition will require tweening an array.</p>
<p style="text-align: justify;">If you&#8217;re not familiar with the ConvolutionFilter object, you create one using a matrix array. Creating a ConvolutionFilter with an array like this (0,0,0,0,1,0,0,0,0), will not affect the image to which it&#8217;s applied. Using an array like this however, (-2,-1,0,-1,1,1,0,1,2), will emboss the image.</p>
<p style="text-align: justify;">So, in this example, we will embed an image, create a tween that tweens the members of the array that change and then tweens them back, and add an event listener to that tween so as the tween updates, a ConvolutionFilter has its matrix property updated then the filter is applied to our image.</p>
<p style="text-align: justify;">Because this script is so completely different from all the others, I will present it in its entirety below:</p>
<pre>package  {

	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.filters.ColorMatrixFilter;
	import flash.filters.ConvolutionFilter;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;

	import org.libspark.betweenas3.BetweenAS3;
	import org.libspark.betweenas3.easing.Quad;
	import org.libspark.betweenas3.events.BetweenEvent;
	import org.libspark.betweenas3.tweens.ITween;

	[SWF(width='540', height='450', backgroundColor='#000000', frameRate='60')]
	public class MatrixTween extends Sprite {

		[Embed(source = "../assets/prague.jpg")]
		private var JPG:Class;

		private var _image:Sprite;
		private var _tween:ITween;
		private var _tf:TextField;

		private var _matrixArray:Array = [ 0, 0, 0, 0, 1, 0, 0, 0, 0];
		private var _convFilter:ConvolutionFilter = new ConvolutionFilter(3, 3, _matrixArray);

		// IDENTITY ARRAY:	(0,0,0,0,1,0,0,0,0)
		// EMBOSS ARRAY:	(-2,-1,0,-1,1,1,0,1,2);

		public function MatrixTween() {
			_image = new Sprite();
			_image.addChild(new JPG());
			addChild(_image);

			initText();
			initTween();

			stage.addEventListener(MouseEvent.CLICK, toggleTween);
		}

		private function initText():void {
			var fmt:TextFormat = new TextFormat("_sans", 12, 0xFFFFFF);
			_tf = new TextField();
			_tf.selectable = false;
			_tf.mouseEnabled = false;
			_tf.autoSize = TextFieldAutoSize.LEFT;
			_tf.defaultTextFormat = fmt;
			_tf.text = "Click to start/stop tween.";
			_tf.x = Math.round(stage.stageWidth * .5 - _tf.width * .5);
			_tf.y = Math.round(stage.stageHeight - _tf.height - 10);
			addChild(_tf);
		}

		private function initTween():void {
			var t1:ITween = BetweenAS3.tween(_matrixArray, { 0: -2, 1: -1, 3: -1, 4:1, 5:1, 7:1, 8:2 }, { 0:0, 1:0, 3:0, 4:1, 5:0, 7:0, 8:0 }, 2.0, Quad.easeInOut);
			_tween = BetweenAS3.serial(t1, BetweenAS3.reverse(t1));
			_tween.addEventListener(BetweenEvent.UPDATE, applyFilter);
		}

		private function applyFilter(event:BetweenEvent):void {
			_convFilter.matrix = _matrixArray;
			_image.filters = [_convFilter];
		}

		private function toggleTween(event:MouseEvent):void {
			if (_tween.isPlaying) {
				_tween.stop();
			} else {
				if (_tween.position == _tween.duration) {
					_tween.gotoAndPlay(0);
				} else {
					_tween.play();
				}
			}
		}
	}
}</pre>
<p style="text-align: justify;">Notice how the index numbers of the array members are used as property names. Compiling the .swf will yield the results below:</p>
<p><object data="https://www.thetechlabs.com/wp-content/uploads/2009/07/matrixtween.swf" type="application/x-shockwave-flash" width="540" height="450"><param name="quality" value="high" /><param name="id" value="matrixtween" /><param name="name" value="matrixtween" /><param name="src" value="https://www.thetechlabs.com/wp-content/uploads/2009/07/matrixtween.swf" /><param name="bgcolor" value="#000000" /></object></p>
<p style="text-align: justify;">In conclusion, even though still in version 0.00 (Preview) at the time of writing this, BetweenAS3 is already a powerful tween engine addition to any actionscript coder&#8217;s arsenal. As always, I always recommend using the right tool for the job and BetweenAS3 may certainly not be right for every time a tween is required, but at the cost of a measly 14k or so, it is definitely worth keeping handy. For more information regarding BetweenAS3, check out <a href="http://www.be-interactive.org/works/20090428/slide_betweenas3.html" target="_blank">this slide show</a> created by the engine&#8217;s author.</p><p>The post <a href="https://www.thetechlabs.com/tech-tutorials/flash/getting-started-with-betweenas3/">Getting Started With BetweenAS3</a> first appeared on <a href="https://www.thetechlabs.com">TheTechlabs.com</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.thetechlabs.com/tech-tutorials/flash/getting-started-with-betweenas3/feed/</wfw:commentRss>
			<slash:comments>15</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Object Caching 99/206 objects using Redis
Page Caching using Disk: Enhanced (SSL caching disabled) 
Lazy Loading (feed)
Minified using Disk

Served from: www.thetechlabs.com @ 2026-05-30 13:11:00 by W3 Total Cache
-->