Create a Stylish 3D Interface with Papervision3D: Effects and Submenus


In the second and final part of this tutorial, we'll complete our 3D menu interface with special effects and submenus, including 3D components like radio buttons.

Continued from Part 1: Structure...

Also available in this series:

  1. Create a Stylish 3D Interface with Papervision3D: Structure
  2. Create a Stylish 3D Interface with Papervision3D: Effects and Submenus

Step 38: Creating a Custom Distortion Effect in 3D

Go back to the preview link and stare at MainMenu3D for a few seconds to see this effect in action. This class will apply distortion to all the DisplayObject3Ds inside MainMenu3D. Its list of responsibilities are:

  1. generate a new custom Bitmap drawing to use as DisplacementMapFilter for every timer loop
  2. add a random Timer that loops and applies the filter to the assigned array of ViewportLayers
  3. play a sound every time the effect is applied
  4. provide external access to activate and deactivate the effect

This class will do its work without the need to be added to the display list. It also doesn't extend any other class.

Create a new class and name it DistortionEffect3D and save it with the rest of the classes. Paste the code below into it:

Again, see the comments next to each property for a brief description.

Step 39: DistortionEffect3D Constructor

Add the code below inside the constructor method:

Here, the required properties are assigned and saved for reference later then createDistortion () is called.

Step 40: Effect Loop Part 1 - Preparing the Tools

This method will also be called every time the random Timer completes. Add the code below after the constructor:

Every time this method is called, a new BitmapData image is constructed from the Shape object. That BitmapData is then applied to a Bitmap which is then used as the base image for the distortion effect. At the end of the method, the Timer is assigned a different duration anywhere between 5 and 20 seconds and repeats only once.

Step 41: Effect Loop Part 2 - Application

Once the Timer completes, TweenLite is then used to apply the effect, play the sound, and when finished, call the createDistortion () method to start all over again. Add the code below:

If the _active property is set to true, the filter is applied, and if not, the loop is just started again skipping the effect. Let's add the 2 helper methods next:

The playSound () method plays the sound with matching volume for the intensity of the effect. $scaleX is anywhere between 50 and 100 so the sound's volume will range from 0.5 and 1. The assignFilter () method is called every time TweenLite updates and applies the transformed filter to all the ViewportLayers in _viewportLayerArray.

Step 42: External Control

As you've seen with the preview link, once a button is clicked, MainMenu3D leaves the scene and gets replaced by either the game or a submenu. When this happens, we won't need the effect until MainMenu3D is back in the scene again. The following two methods take care of this for us:

And that concludes the DistortionEffect3D class.

Step 43: Milestone Applying Distortion to MainMenu3D

Go back to MainMenu3D's setMenuEffect () method and add the code below to it:

Don't forget to uncomment the _distortion property declaration at the top of the class. All we do here is go through all the UIComponent3Ds inside MainMenu3D to access their ViewportLayers and put them all inside an array. Once all the ViewportLayers you want the distortion effect applied to have been collected, its time to instantiate DistortionEffect3D and pass in the array as the single parameter. Next, go to MainMenu3D's prepareIntro () method and add code below at the very start:

Now add the code to deactivate it when MainMenu3D is not the current menu. Go to MainMenu3D's startExit () method and add the code at the very start:

Go ahead and run the program. The sample result is linked here. You should see and hear the distortion effect repeat anywhere between 5 and 20 seconds.

Step 44: More Effects - StaticGlow3D

For now, let's focus on adding 3 more effects to improve our interface. First up, the StaticGlow3D class which will add the transparent glow for all the menus. We want our menus to look holographic and this class will provide that effect. Its responsibilities are:

  1. draw a BitmapData as the base image for the effect
  2. provide external control to animate in and out
  3. have 2 different ways to animate the effect and match how the active menu opens

Create a new class named StaticGlow3D and extend DisplayObject3D for it. Paste the code below replacing everything inside it to start:

All the variables are declared protected for inheritance. StaticGlow3D will serve as the super class for both the BackgroundGlow3D and BackgroundNoise3D classes. Its constructor calls super () to assign the name for the DisplayObject3D. The _viewport property is then set and init () is called.

Step 45: Preparing the Visual Asset for the Effect

The init () method creates the image used for the effect. Add the code below after the constructor:

As you see with the comments, The 3D Plane is given a special material made out of 2 BitmapDatas. It's probably simpler if we just used a VectorShape3D object and applied blur to it, but this is fine too. You'll see how we can use this same principle later and create static effect with faded corners.

Step 46: Control and Use

Once everything has been prepared, all we have to do is externally control fading the effect in and out. Add the last 2 methods next:

The flash () method is called before a menu appears into the scene. It checks the value of $sideways and animates based on that value. If $sideways is true, like when we go to the settings menu, the glow's scaleX property is compressed then tweened out to normal. If $sideways is left as its default value - false, like when we go to the main menu, the glow's scaleY property is compressed then tweened out to normal. The second public method, fadeOut () does the opposite and is called when a menu is closing. Only this time the image thins before fading out.

Step 47: Milestone Testing StaticGlow3D

First, go at the top of the class where properties are declared and add _staticGlow as an instance property. Go into Tester's init () method and add a call to addStaticGlow () before the call to addMainMenu (). Then add the code below after the init () method:

Next, go inside both the addMainMenu () and the onMenuExitComplete () methods. Add a call to _staticGlow.flash () after the call to MainMenu3D's startIntro () method. Then go inside the beginning of the navigateTo () method and add a call to _staticGlow.fadeOut (). The methods should look like below:

Run the program and you should have the result shown on the link here. Alright! Next is the StaticNoise3D class.

Step 48: Applying Static Noise in PV3D

If you want to see this effect in action, go to the preview link posted above and change the Quality to best in the settings menu. This class will extend StaticGlow3D and apply some modifications. Here is the list:

  1. draw an image to use as its static noise//same as the superclass
  2. provide external control to animate in and out //same as the super class
  3. have 2 different ways to animate the effect and match how the active menu opens //same as the super class
  4. provide external access for update calls to apply random noise to the BitmapData for the effect //additional responsibility

Create a new class and name it BackgroundNoise3D, have it extend StaticGlow3D.
Copy the code below and replace the contents of the new class with it:

Since we're using OOP, we only need to import what we need to modify the class. The class constructor takes in the same 2 parameters and passes them both to StaticGlow3D.

Step 49: Preparation, Control and Use

As shown in the list of responsibilities, everything is pretty much the same as the super class, except for how the effect is generated and 1 additional responsibility. The update () method will take care of applying noise to the BitmapData that is used for the effect. This I think, is the smallest class in this project. Add the code below:

You can get more information about the BitmapData.noise () method here. That's it. BackgroundNoise3D is finished! Another good example of the power of OOP.

Step 50: Milestone Applying BackgroundNoise3D

Inside Tester's init () method, add a call to addBackgroundNoise () just below the addStaticGlow method call. Copy the code below after the actual addStaticGlow () method:

Next, it's controlled the same way as StaticGlow3D. Add the codes below according to the comment posted above each code:

Lastly, we need to repeatedly call its update () method in order to reflect the changes applied for the noise effect. Add the code below inside the onRenderTick () method:

Don't forget to declare _backgroundNoise as an instance property of type BackgroundNoise3D at the top of the class. Now you can run the program. It should look like the link here.

Step 51: Yet Another Effect - BackgroundGlow3D

It still felt like the menu was incomplete so I thought, why not add a striped background that glows before the menus animate in. Its list of responsibilities are:

  1. draw an image to use as its striped glow //modified from the super class
  2. provide external control to animate in and out //modified from the super class
  3. play a sound every time the effect is applied //additional functionality

Create a new class and name it BackgroundGlow3D. This too will extend StaticGlow3D. Copy the code below replacing everything in the new class:

There are more imports involved here because of the few changes to draw the image for the effect. But the constructor remains identical.

Step 52: Preparing the Visual Asset

The dimensions are the same but this time, we're drawing lines with spaces between into the BitmapData. Add the code below after the constructor:

See the comment in the code where it says 'start of changes'? That part adds sprites with single lines drawn on them and adds them into _parentSprite. The _parentSprite is then used as the background for the BitmapData used as material for the 3D Plane.

Step 53: External Control

This effect will only have one external control. The flash () method will be called every time the menu is about to load. Unlike the static glow effect which stays with the menu, this effect will fade out as soon as it flashes. This makes the fadeOut () method irrelevant. Add the code below:

Overridden from the super class, the $sideways parameter is required but has no application. The modification here also includes playing the 'backgroundFlash' sound effect loaded from the CustomLoader instance. This completes the BackgroundGlow3D class. Since we didn't override the fadeOut () method, it is still available for this subclass. If you want to completely disable it, just override the method and leave it blank without passing a call to the superclass.

Step 54: Milestone Adding the BackgroundGlow3D

Go back inside Tester's init () method and add a call to addBackgroundGlow () just below the addBackgroundNoise () method call. Copy the code below after the addBackgroundNoise () method:

Next, go to the addMainMenu () and onMenuExitComplete () methods and add a the code below:

Again, make sure _backgroundGlow is declared as an instance property of type BackgroundGlow3D at the top of the class. every time MainMenu's startIntro () method is called, the flash () method for all the effects should be called as well. Now run the program. The result can be viewed here.

Now it's riddled with effects! =) Later, we'll add an option for turning the BackgroundNoise3D on or off.

Take a break and come back when you're ready to start building the first of two submenus, the AboutMenu3D.

Step 55: Adding an About Submenu - AboutMenu3D

This DisplayObject3D will be a modified subclass of MainMenu3D. It will contain a masked 3D scrolling text, 2 buttons to scroll this text, and a back button to go back to the main menu. Its list of responsibilities are:

  1. create 2 borders to act as casing for the contents of this menu //same as the superclass
  2. create and add the 3 buttons and add listeners for them //modified from the superclass
  3. prepare for intro animation //modified from the superclass
  4. add effects and animations //modified from the superclass
  5. add 3D scrolling text //new responsibility
  6. add a mask for the 3D scrolling text //new responsibility
  7. provide external control for playing intro and exit animations //same as the superclass
  8. manage activating and deactivating the buttons //same as the superclass
  9. dispatch the custom event triggered by the back button so that UserInterface3D can load the MainMenu3D back //additional responsibility

Go back to the preview link here and click the About button to see this menu. It looks simpler than MainMenu3D because of the fewer buttons it has but its internal working is a bit more complex. Create a new class and name it AboutMenu3D. Have it extend MainMenu3D. Once the class file is open, just copy and paste the code below replacing the content of the class.

The instance variables are all declared private only because we won't be extending this class any further for this project. But if think you might need to extend it later for your own projects, feel free to change the modifiers appropriately.

Step 56: Initialization

To modify the contents of this menu, properties need to be changed from the init () method. Add the code below after the constructor:

New values are assigned for the inherited properties that need changing before calling the superclass' init () method. The new responsibilities are then taken care of. Next, we override the createFrame () method to change how the two borders' look. Add the code below after the init () method:

The borders are drawn completely differently from the superclass. Next, add the modified addButtons () method:

It only looks huge because of the space taken by the drawing coordinates. All it does is add the 3 buttons, position them, and assign the appropriate listeners based on the named given for them. See the comments for descriptions. The next method overrides the superclass and is left empty so the distortion effect is not applied for this menu:

That takes care of all the inherited methods triggered from init (). Now, for the two additional functionalities. Add the code below:

The method is called before createText () to make the mask available and ready. A 3D Plane is created with the same width as the borders and a height of 550. To use a 3D Object as mask, we need to access its ViewportLayer property. Next, add the createText () method:

This is similar to how we created the text for Button3D or UIComponent3D. Masking is applied by assigning the 3D Plane's ViewportLayer as mask for the Text3D's ViewportLayer.

Step 57: Extending the Inherited Intro Animation

Right now, when startIntro () is called, the 2 borders and the 3 buttons will animate in the same way as MainMenu3D. Add the code below to add the 3D text when the intro starts:

The initializeButtons () method is called when the borders reach their open positions. Check MainMenu3D's startIntro () method for a refresher. Here, we have it call the superclass' method to handle the 3 buttons then tween the 3D text in. Next, add the modified showButtons () method called from MainMenu3D's initializeButtons ():

Here, we do the same to it as the superclass the we make sure that _scrollDown and _scrollUp are set to false . We had to do this here because the other 2 buttons are used for scrolling and uses onButtonOver () listeners. If those listeners get triggered by showButtons (), the 3D text will start scrolling.

Step 58: Handling Mouse Events

Everything is handled internally except when the back button is clicked. Add the code below:

This informs UserInterface3D to go back to the main menu. Let's now add the scrolling functionality:

These two methods assign the value for _scrollUp and _scrollDown properties which will be used as marker if the text should be scrolled. You'll see later when we get to the additional onUpdate () method.

Step 59: Extending the Inherited Exit Animation

Nothing fancy for the startExit () method. We just make the 3D text invisible then let the superclass handle the rest. The hideButtons () method is overridden to make sure _scrollUp and _scrollDown are both set to false again. If not done so here, the 3D text will scroll down while the menu is removed. Add the code below:

If you're unsure how these two methods interact, go back and review MainMenu3D's startExit () method.

Step 60: Scrolling The 3D Text

Just like BackgroundNoise3D, visual changes can only be applied from the update () method.

This method will get called repeatedly from UserInterface3D's onRenderTick () method. It constantly checks the values of _scrollUp and _scrollDown and adjusts the 3D text's 'y' position. That completes the AboutMenu3D class.

Step 61: Milestone Testing AboutMenu3D

Go back to Tester's init () method and comment out the calls to addBackgroundNoise and addMainMenu, then add a call to addAboutMenu () at the bottom of the method. Next add the code below the init () method:

Don't forget to declare _aboutMenu as an instance property of type AboutMenu3D at the top of the class. As you can see, its very similar to MainMenu3D's instantiation. This time, we only listen for a MainMenu3D.BACK_TO_MAIN and scale the two effects 1.5 percent the original size. Next, modify the navigateTo () method as shown below with comments:

Make sure to comment out the call to _backgroundNoise.fadeOut () method call since we're not instantiating it this time. Assign a new jump target for the MainMenu3D.BACK_TO_MAIN event at the bottom of the switch statement.

The onMenuExitComplete () will act just the same for AboutMenu3D, thanks to OOP, but you'll also need to comment out the call to _backgroundNoise.flash () inside it.

Lastly, go inside the onRenderTick () method and modify the code according to the comments as show below:

Now run the app and you should see something like this.

Step 62: Adding a Settings Submenu - SettingsMenu3D

We're down to our last menu. By far this menu is more complex than the other two. But to make this class work, we'll need to create 3 new classes - CheckBox3D, RadioButton3D, and ButtonGroup3D. So let's jump out and work on these components. Let's start with CheckBox3D. You'll see the details about SettingsMenu3D when we get to step 75.

Step 63: Creating a New Button3D Type - CheckBox3D

Go to the preview link here and play with the full screen checkbox in the settings menu. Turn it on then turn it off again by clicking on it. Once its on, you can also turn it off by hitting the escape key. It differs from Button3D in a few ways - it consumes the coordinates for drawing differently, the button stays on its 'z' axis when the mouse is over it, the instance will have an extra Boolean variable to hold the value of its current state, the button will have additional external controls to turn it on or off. Its list of responsibilities are:

  1. play sound effects for mouse over and mouse click. //inherited
  2. add an interactive plane that will trigger the mouse events. //inherited
  3. apply effects for mouse over, mouse out, and mouse click. //modified
  4. provide external control for adding and removing mouse event listeners //inherited
  5. provide external control to remove mouse click listeners //inherited
  6. provide external control to turn the button on or off //additional functionality
  7. draw a checkbox using Vectorshape3D //modified from UIComponent3D
  8. store its own state and provide access for it externally //new functionality

Create a new class named CheckBox3D, have it extend Button3D. Replace the code in it with the code below:

Instantiation is the same except for the values inside the _coordinates variable. For drawing, we'll use the Graphics3D.drawRoundRect () method instead of the moveTo () and lineTo () methods we used for the superclass. To maintain uniformity, we'll keep the Number2D _coordinates array and just not use its 'y' property by setting them to 0. See the comments for designation.

Step 64: CheckBox3D Initialization

Once the instance is created and the init () method is called externally, the following 3 methods are triggered. Add the modified setDimensions () method after the constructor:

Next, add the modified createInterface () method:

Here, we totally skip from calling the superclass' createInterface () method. Instead, it calls the new drawButton () method and then goes back to using the superclass' addInteractivePlane () method. Next, add the modified createText () method:

After letting the superclass create the text, CheckBox3D takes over and modifies its location and scale.

Step 65: Drawing the CheckBox

This method first gets called by the createInterface () method and every time the onObjectClick () is triggered. It draws the checkbox based on the current value of _state.

It should remind you of UIComponent3D's createInterface () method. See the comments for its differences.

Step 66: Responding to Mouse Events

For mouse overs, we want the checkbox to still glow but stay on its out position. Add the overridden onObjectOver () method:

every time the checkbox is clicked, we want it to toggle on and off. Add the code next:

First, _state is set to its opposite value; then, the drawButton () method is called to redraw the checkbox. It then dispatches the event and plays the assigned sound for button clicks. There won't be any modifications needed for the onObjectOut () method so we skip that here.

Step 67: CheckBox3D External Access

Here comes the additional functionality to turn the button on or off externally. The turnOn () method will set the value of _state to true then call the drawButton () method:

Next add matching turnOff () method:

These methods will help when you use the checkbox in a group. You'll see its application when we group radio buttons soon.

And last, add access to the checkbox's current state:

Step 68: Milestone Testing CheckBox3D

Go back to the init () method inside the Tester class and comment out the addAboutmenu (), addBackgroundGlow (), and the addStaticGlow () method calls. Still in the init () method, add a call to addCheckbox () at the bottom. Go into the onRenderTick () method and comment out the call to _aboutMenu.update (). Next, add the new method below the init () method:

Its very important to change the lineAlpha value, remember, this defaults to 0 from the UIComponent3D superclass. Next, add the callback for the checkboxClicked listener:

We'll use this information to change the fullscreen mode later. For now, let's just do a trace statement. Now run the app and you should see something like the picture below. The trace should show the value of the checkbox's state every time the checkbox is clicked. The position feels a bit off but we'll fix that when we add it as a component for the settings menu.

Step 69: Making a RadioButton3D out of CheckBox3D

Here's the preview link again if you'd like to see how the radio button works. Go to the settings menu and play with the group of radio buttons.

Create a new class named RadioButton3D, have it extend CheckBox3D and save it. Replace the contents of the new class with the code below:

There will only be 3 visual modifications from CheckBox3D so the list of responsibilities are the same. One, we need to change the scale and position of the text/label. Two, we use the Graphics3D.drawCircle () method instead of the Graphics3D.drawRoundRect () for drawing the radio button. And three, since we're using Graphics3D.drawCircle (), the setDimensions () method will have to be modified to use the same value for its width and height properties. Add the code below for the overridden setDimensions () method after the constructor:

Next, add the modified drawButton () method:

Now add the modified createText () method:

And that completes the RadioButton3D. Thank goodness for inheritance. You can test it if you like, just change the part where it says new CheckBox3D with new RadioButton3D inside the addCheckBox () method in Tester and you're all set. Or you can wait until we've created ButtonGroup3D to test a group of radio buttons which we're doing next.

Step 70: ButtonGroup3D

This class will act as both manager and container for the group of radio buttons. It takes in 4 required parameters and 1 optional when instantiated. The parameters are - the name/label for the group, the viewport, an array of button names, and the default state. The last optional parameter was created to provide an option for aligning the buttons either vertically or horizontally. Unfortunately, due to time constraints, I was only able to apply the horizontal alignment. I'll leave it up to you to finish with the vertical option. Following is its list of responsibilities.

  1. create text to use as its name and label
  2. create a set of radiobuttons that will work together as a group
  3. manage the buttons so that only one will be on its 'on' state at all times (while the rest are in their 'off' state)
  4. dispatch a STATE_CHANGED event when the state of the group changes (ie. when a different button is turned on)
  5. hold the value of the group's current state
  6. make each button glow just like their superclass when the menu is about to close

Create a new class named ButtonGroup3D and have it extend DisplayObject3D. Copy the code below to replace all of the content inside the new class:

Again, just like the other classes during instantiation, parameters are passed in and saved, then the init () method is called to prepare its contents of components.

Step 71: Initialization - ButtonGroup3D

The init () method calls two other methods to prepare the text and the buttons for the group. Add the code below after the constructor method:

The next method for creating text is the same as we've done for the other classes, only the position and scale have been modified. Add the code below the init () method:

Next. add the addButtons () method:

The top portion of the loop goes through the number of button names saved in _buttonNames array. It then sets each button's visual properties and adds them as children at the same time saving reference for each in _buttonArray. The middle part of the method checks if the button's name matches the assigned default state which was also passed in the constructor. If it is, the button is turned on and then deactivated from listening for events. If not, the button is left on its off state and it is activated to listen for events. The last part simply aligns the buttons horizontally. Each button is assigned a listener which will only get triggered if the button is active. ButtonGroup3D will handle this internally before it is dispatched.

Step 72: Managing Click Events in ButtonGroup3D

Below is the callback that will handle the click events for all the active buttons. Add the code after the addButtons () method:

Here, the state for the button group is assigned the name of the active button that was clicked. Then all the buttons are looped through to see if its the button that was clicked. If it is, it is deactivated from listening for clicks, if the button isn't the same button that was clicked, it is turned off and activates it for click events again. When the loop finishes, the STATE_CHANGED event is dispatched to inform its parent container - SettingsMenu3D.

Step 73: External Access

The flash () method will make all the buttons flash when the menu is about to close, You'll see how this is applied when we get to SettingsMenu3D. Add the code below:

And finally, add the code below to provide access for the button group's current state:

Step 74: Milestone Testing ButtonGroup3D

Go back to the Tester class inside the init () method and comment out the call to addCheckbox () then add a call to addButtonGroup () below it. Paste the code below after the init () method:

Right now, with the current setup, it's best to use between 3 and 5 characters for the button labels. When ButtonGroup3D is instantiated, it is passed the name for the button group, the viewport, an array of button names in string format which will be used as names for each button and also as different states for the button group, and the default state which could be any of the 3 strings inside the button array. Once instantiated, it needs to listen for the custom event ButtonGroup3D.STATE_CHANGED to know when an active radio button was clicked. The rest positions the ButtonGroup3D instance in the center and adds it into the scene. Next add callback for the onStateChange event listener:

Here, we just do a quick trace to see if ButtonGroup3D does what its supposed to. Run the app to see the result. It should look exactly like this.

Step 75: Building SettingsMenu3D

Just like AboutMenu3D, this class will also inherit from MainMenu3D. This will be our last menu and once we get this done, we'll work on the UserInterface3D class to finally put everything together. This menu will allow the user to modify a few internal settings for the flash player, add or remove an effect, and save the chosen level for the game. Its list of responsibilities are:

  1. create 2 borders to act as casing for the contents //modified
  2. create and add the buttons and add listeners for them //modified
  3. prepare for intro animation //modified
  4. add effects and animations //modified
  5. provide external control for playing intro and exit animations //same as superclass
  6. manage activating and deactivating the buttons //modified
  7. dispatch custom events triggered by the buttons so that UserInterface3D can apply changes to the flash player settings or remove an effect. //modified
  8. save game level available for external access //additional functionality
  9. save stage quality available for external access //additional functionality

Create a new class named SettingsMenu3D and have it extend MainMenu3D. Replace the contents of the class with the code below:

If you check this menu out from the preview link here, you'll see that it contains 1 checkbox which toggles the fullscreen mode, 2 radio button groups which handle the stage quality and game level, and 1 back button to return to the main menu. The first 3 properties from the top of the class declaration will serve as game levels for our program. The private property _level will store 1 of these 3 levels at all times and will have a default value of LEVEL_NORMAL. The _quality property will hold the value for stage quality and will default to StageQuality.MEDIUM. The last 2 properties will hold instances of ButtonGroup3D, 1 for the stage quality and another for the game level. The constructor only needs to pass the information to the superclass.

Step 76: Initialization for SettingsMenu3D

Once the init () method is called by the superclass' constructor, we override it from SettingsMenu3D and make changes to some inherited properties. The _buttonPosY is given only 2 'y' positions for the buttons to follow, _buttonTitles is assigned a new array containing new information to make as buttons. Then the superclass's init () method is triggered to continue the process. Add the code below the constructor method:

From the superclass' init () method, the following 3 methods are called. The first being the createFrame (), which is overridden to change the menu borders' shape and colors. Add the code after the init () method:

The addButtons () method has been altered quite a bit. I've divided the method into 2 parts to make it easier to understand. It starts by getting _buttonArray ready to store references for the buttons we're about to create. _buttonTitles is then looped through and checked if the name matches '<<'. If it does, a Button3D instance is created and added at the center of the menu. Its text is centered manually inside the button's visual representation. If the name doesn't match '<<', a CheckBox3D instance is created instead and placed at the top of the menu. The instance's colors are also modified before calling the button's init () method. Still within the loop, each button is assigned a listener for clicks, positioned on 'y' axis based on the current iteration's value in _buttonPosY, saved a reference in _buttonArray and added as SettingsMenu3D's children. The second part, adds 2 ButtonGroup3Ds for control of the stage quality and game level. We also have a different listener assigned for these button groups since they need to be handled differently. Add the full code next:

And the setMenuEffect () method, the last method called by init (). We override it here and apply no effect. I'll pass this as an optional homework to you guys if you want to use the DistortionEffect for SettingsMenu3D. Remember, it takes in an array of viewport layers to apply the filter to. Add the code below:

Step 77: Handing Intro Animation - SettingsMenu3D

Since the 2 borders are opening in a horizontal motion, they are positioned a little differently than how we did it for the superclass. Both _gameLevel and _stageQuality are set fully transparent ready for the intro animation. Add the code below after the setMenuEffect () method:

The startIntro () method only differs from the superclass by how the 2 borders open up. Here we tween the borders 'x' position to move them away from each other. Add the code next:

The initializeButtons () method doesn't need to change so we leave it as it is in the superclass.

The showButtons () method calls its super method and then takes over to manage the intro for the 2 ButtonGroup3Ds. Add the code next:

Step 78: Handling Mouse Events - SettingsMenu3D

The onButtonClick () method will handle the clicks for the fullscreen checkbox and the back button. It has the same functionality as the superclass. If the name of the button clicked is '<<', it dispatches a MainMenu.BACK_TO_MAIN and starts the exit animation. If the fullscreen checkbox was clicked, it dispatches an InteractiveScene3DEvent.OBJECT_CLICK. Toggling fullscreen mode will be done through the UserInterface3D so all we do here is inform it about the event. Add the code below:

The onStateChange () method will handle clicks for the 2 button groups. Here it checks the state of the event's target and responds accordingly. Then if the target's name is 'Quality', it propagates the event up for UserInterface3D to consume. We won't need to propagate the event if it came from 'Level' since it will be checked by the Main document class before loading the sample game sprite. So we just trace it here for testing. Add the code below:

Step 79: Handling Exit Animation - SettingsMenu3D

When the back button is clicked, it calls the superclass' startExit () method. We don't need to override it here since it will work the same way for SettingsMenu3D as it does for the superclass. Once the hideButtons () is called by startExit (), we flash the 2 button groups so they exit the same way as the other buttons and then they are made invisible. After the 2 button groups are handled, it passes the call to the superclass to remove the checkbox and back buttons.

The hideObject () method is also just inherited without modification and once it completes, it calls the closeNav () method modified here. Again, it only differs from the superclass in the way the 2 borders close. Add the code below:

Step 80: External Access - SettingsMenu3D

Here are the few additional functions for SettingsMenu3D. Add the code below:

These 3 methods provide external access for the 3 configurations stored in SettingsMenu3D. These will be accessed by the Main and UserInterface3D classes. Next, add the exitFullScreen () method:

This method will get called when the user hits the escape key, that way, even if SettingsMenu3D isn't the active menu, it could still be turned off if the user decides to exit fullscreen. You'll see how this is utilized when we test SettingsMenu3D in the next step. We'll have the Main document class listen for this event so we can sync the fullscreen checkbox even if the UserInterface3D isn't active on the stage.

Step 81: Milestone Testing SettingsMenu3D

Alright, There's quite a few modifications we need to make to test SettingsMenu3D. We'll work our way from the top. Go inside Tester's package declaration by the imports and add the code below:

Next, go to the constructor method. Add the code below after the call to startRendering ():

This event will let us know when the stage is available for access. Next, after the constructor, add the callback method below:

The FullScreenEvent listener will be used to inform SettingsMenu3D to set the full screen checkbox to its 'off' state. The setStageQuality () method will set the quality of the stage based on the default stageQuality in SettingsMenu3D. Add the two methods that respond to them next:

When you look in the Main document class, you'll see that Tester is instantiated before it is added to the stage. So applying changes to the stage quality right after instantiating SettingsMenu3D will not work. The "stage" object will simply not be available yet. So go inside the init () method for Tester and uncomment the addStaticGlow () and addBackgroundGlow () method calls. Next, comment out the call to addButtonGroup () and add a call to addSettingsMenu () below it. Then add the code below after the init () method:

Make sure _settingsMenu is declared as an instance variable of type SettingsMenu3D.

We have 4 different listeners assigned to the SettingsMenu3D instance. The navigateTo () method is triggered when the '<<' button is clicked inside SettingsMenu3D. The onStateChange () method is called when the button group named 'Quality' changes state replace the content of the onStateChange () method as shown below. The toggleFullscreen () method is triggered whenever the fullscreen checkbox is clicked and the onMenuExitComplete () method is triggered when the menu finishes its exit animation. We set the scaleX for both _staticGlow and _backgroundGlow to 1.25 times their original size to fit the menu. After calling the startIntro () method, we call the flash () methods for both _staticGlow and _backgroundGlow passing in a Boolean value of true to make them flash horizontally. Add the 2 additional callback methods for SettingsMenu3D below:

We need to check if the stage property is available before changing its display state. We apply it here to get it ready when we use it for UserInterface3D. There, if the game is active, UserInterface3D will be removed from the stage and will temporarily lose its stage property. the onStateChange () method won't need to check for the stage since, this can only get triggered from within SettingsMenu3D. Lastly, Go to the onMenuExitComplete () method and pass in a value of true for the call to _staticGlow.flash () method. Do the same for the call to _staticGlow.fadeOut () method call inside the navigateTo () method.

Now run the program. You should have a result like the link here. For those using the Flash IDE, fullscreen will not work when you run it from Flash. You'll have to open the SWF file directly with Flash Player.

Step 82: Putting It All Together

Alas, the final class for our project. The UserInterface3D class is the biggest of them all. It will inherit from BasicView just like the Tester class. Think of this class as the main controller, everything will be handled by this class. Go back and check out the link here for the fully functional preview. Play with it for a moment to get a feel of how UserInterface3D manages everything. Here is its list of responsibilities:

  1. set up the scene - ie. set the sort mode to index sort, set the camera's properties etc...
  2. create all the menus and load each of them appropriately
  3. manage all events that come from the 3 menus and apply the changes for their designation - ie. toggle fullscreen, inform the Main document class to load the game
  4. handle the position of 1 large DisplayObject3D that will house all 3 menus
  5. add the 3 effects - background noise, background glow, and static glow and control them
  6. provide external control for loading back the main menu when the game sprite is removed
  7. provide external control for to manage the full screen checkbox in SettingsMenu3D when fullscreen is disabled while the game sprite is active

I've divided the class into two large parts, initialization and event handling. Create a new class named UserInterface3D and have it extend BasicView. Paste the code below replacing all the code inside the new class:

There's quite a few properties for this class but we've gone through most of them in the Tester class. See the comments for property descriptions. The constructor is set to trigger the init () method only when the stage is available. This avoids errors when trying to access the "stage" object.

Step 83: Initialization - Part 1

Once the init () method is called, it triggers a series of methods that break down the initialization process by responsibility. Add the code below after the constructor:

These methods are not interchangeable, you'll run into bugs if try to call setStageQuality () before initializeInterface (). Add the setUpScene () method after the init () method:

This is where you can apply modification for the camera, scene, and viewport. Here, we make sure the camera is not looking at any single DisplayObject3D. For the viewport, the sort mode and interactive properties are modified to accommodate our requirements. This is also where we add the 1 DisplayObject3D that will house the 3 menus. I decided to do this to have better control of the 3 menus. The effects will be added directly into the scene since they won't need to move around like the menus would. Next, add the initializeEffects () after the setUpScene () method:

This method simply adds the 3 effects into the scene with slight differences for their 'z' positions. Add the code below for the initializeInterface () method next:

The initializeInterface () instantiates all of the menus and then loads the MainMenu3D instance into the scene. I'll discuss more about the individual methods when we get to them. Next, add the setStageQuality () method after initializeInterface ():

Here, the stage's quality is set to the default stageQuality of the SettingsMenu3D instance. This method is called every time the SettingsMenu3D instance dispatches a ButtonGroup3D.STATE_CHANGED event. It also checks if the stage's quality is set to 'BEST'; if it is, the BackgroundNoise () instance is added into the scene, if not, it is removed from the scene. We provide it as an option for the user to turn it on or off since it is processor intensive.

Step 84: Initialization - Part 2

Let's now go over adding all the menus. First, the initMainMenu (). Add the code below:

Everything is the same as how we used them in the Tester class except for the 'x' position we apply to each of the menus. Also, notice we didn't add the menu into _grandObject3D. This will be done when addInteractivity () is called passing in the correct menu to load. Next add the initSettingsMenu () method:

The 'x' position is set to the absolute value because the _grandObject3D will be the one moving towards the true value. A good explanation would be: "if the current 'x' position is 0 which is where _mainMenu is and we need to go to _settingsMenu's 'x' position of 2000, _grandObject3D will move to an 'x' position of -2000 centering _settingsMenu in the scene." Remember, all the menus are contained inside _grandObject3D with different 'x' locations. Next, add the initAboutMenu ():

Once all the Menus have been added, the event flow goes back to the initializeInterface () method and calls addInteractivity (). Add the code below:

This is how the _mainMenu is primarily loaded into the scene. This method also gets called every time a button is clicked to change menus. It works with the loadTargetMenu () method discussed later in the event handling section. Here, the _currentMenu property is assigned the menu that needs to load. _grandObject3D then adds that menu as its only child to make it available for the scene. The menu is then added a listener for when it has fully closed. This information will allow the replacement menu to load only after the previous menu has completely exited. After calling its startIntro () method, it checks to see the menu's type; if it is of type SettingsMenu3D, it tells the _staticGlow and _backgroundNoise to both animate in horizontally; if the menu's type is anything else, the two effects will animate in vertically. And that completes the initialization section.

Step 85: Event Handling

The rest of the UserInterface3D's functionality from here on are triggered by events. Add the code that responds when the fullscreen checkbox in _settingsMenu is clicked:

Next, since the fullscreen can change when the user hits the escape key while the game sprite is active, (this means the UserInterface3D instance has been temporarily removed as a child of the Main document), we'll need to inform _settingsMenu to uncheck the fullscreen checkbox when this happens. Add the code below:

Now, whenever the Main document class instance detects the user exited fullscreen, the SettingsMenu3D instance is informed. We'll add this listener to the Main document class when we finish building UserInterface3D. Next, add the "level" implicit getter method:

This will allow the Main document to access the level chosen in _settingsMenu and inform the game sprite about it. Next, add the navigateTo () method:

This works the same way as we used it for the Tester class. Here, we use it to scale the effects and change the values for _closeApp and _loadGame properties. Both will be used by the loadTargetMenu () method discussed next. Add the code below:

This method gets called after the current menu has completely exited. That's why the webpage/standAlone player terminates only after the menu fades out when you click the exit button. If _loadGame is true, a UserInterface3D.START_GAME is dispatched to inform the Main document to load the game sprite. Once the event has dispatched, _loadGame is set to false again and the method is terminated prematurely. The game Sprite will be loaded and UserInterface3D will then be removed from the stage. This is also the method the Main document will call to load the UserInterface3D instance back into the stage.

Next, add the onRenderTick () method below:

The same application from how we used it in Tester. But instead, the camera stays in the same place and everything else is rotated and moved.

Step 86: Final Additions for the Main Document Class

As I've mentioned before, The Main document class will have some part of controlling the game Sprite and the UserInterface3D. Go back in the Main class and add 3 more instance properties:

Next, go inside the onLoad () method and comment out anything that deals with the Tester class then add the code below.

This simply adds a UserInterface3D instance into the stage and listens for when the start game button is clicked. Next, add the startGame () method:

Here, we load a simple sprite that represents your game. The text within it gets the information directly from the UserInterface3D instance, as well as displaying whether the SWF is loaded in a plugin or standalone Flash Player. It is assigned a click listener so the Main document class can load the UserInterface3D instance back again when this sprite is clicked.

The method below takes care of reloading UserInterface3D:

Congratulations! You have successfully completed the project! Now run the fully functional application to see the result.


Like I said in my Atoms tutorial, Papervision3D is a very straightforward and powerful tool. Experiment with it and you will come up with all kinds of cool 3D effects ranging from simple simulations to sophisticated interfaces. You now have under your belt experience of creating a GUI framework loaded with effects using PV3D. Along with the source download is a simple drawing application I've put together to help with your interface design. It comes with a short note on how to use it.

I hope you enjoyed reading this tut.

As always, for any questions, suggestions, or concerns, please post a note in the comments section.

Thanks for Reading!