10. Advanced Interactivity


Today we're going to learn how to handle more advanced interactivity. We'll be making something like this:

Click on a face of the cube to zoom into it. Click it again to make the cube spin again.

So, in this tutorial I'm going to show you exactly how to work out which material has been clicked on the cube and act accordingly.

If you haven't read it already, I strongly suggest that you read the Basic Interactivity tutorial first or you'll probably miss something.

Ok, so on to the code.

Firstly we'll want six materials to apply to each face of the cube. Here are mine, although you can create whatever materials you like:

Actionscript:
  1.       private var frontMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/front.jpg");
  2.        private var backMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/back.jpg");
  3.        private var leftMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/left.jpg");
  4.        private var rightMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/right.jpg");
  5.        private var topMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/top.jpg");
  6.        private var bottomMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/bottom.jpg");

So, in this code I'm just loading six images from my server to use as each face.

Also, add the following declaration beneath the texture declarations to hold our Cube object:

Actionscript:
  1. private var cube:Cube;

Now we need to set up the 3d initiation code. In this code we will firstly set all of our materials as interactive, and then give each of our materials a name. This is VERY important to be able to easily find out which material has been clicked. If we didn't give each material a name then we wouldn't be able to easily work out which face has been clicked.

So, add the following code to your init3d() function:

Actionscript:
  1.       override protected function init3d():void {
  2.          // We need to be able to identify each side. We'll do this
  3.          // by asssigning names to each material. During this process
  4.          // we'll also make the materials interactive.
  5.          frontMaterial.interactive = true;
  6.          frontMaterial.name = "front";
  7.          backMaterial.interactive = true;
  8.          backMaterial.name = "back";
  9.          leftMaterial.interactive = true;
  10.          leftMaterial.name = "left";
  11.          rightMaterial.interactive = true;
  12.          rightMaterial.name = "right";
  13.          topMaterial.interactive = true;
  14.          topMaterial.name = "top";
  15.          bottomMaterial.interactive = true;
  16.          bottomMaterial.name = "bottom";
  17.          // ---------------------------------------------
  18.          
  19.          cube = new Cube(new MaterialsList( {
  20.               front: frontMaterial,
  21.               back: backMaterial,
  22.               left: leftMaterial,
  23.               right: rightMaterial,
  24.               top: topMaterial,
  25.               bottom: bottomMaterial
  26.               } ), 500, 500, 500, 3, 3, 3);
  27.          cube.addEventListener( InteractiveScene3DEvent.OBJECT_PRESS, onPress);
  28.          default_scene.addChild(cube);
  29.        }

So, In this code, we've set each material as interactive, given each one a sensible name, and initialised our cube with the materials assigned to the correct faces.

We then add an event listener to trigger the "onPress" event when the cube is clicked, then finally we add the cube to the scene.

We've now got a cube with six materials on it listening for a click event.

Now, the code which will let us work out which face has been clicked:

Actionscript:
  1. private function onPress( e:InteractiveScene3DEvent ):void {
  2.     switch(e.face3d.material.name) {
  3.        case "front":
  4.          // This code will be run when the front face is clicked
  5.        break;
  6.        case "back":
  7.          // This code will be run when the back face is clicked
  8.        break;
  9.        case "left":
  10.          // This code will be run when the left face is clicked
  11.        break;
  12.        case "right":
  13.          // This code will be run when the right face is clicked
  14.        break;
  15.        case "top":
  16.          // This code will be run when the top face is clicked
  17.        break;
  18.        case "bottom":
  19.          // This code will be run when the bottom face is clicked
  20.        break;
  21.     }
  22. }

Pretty self explanitary. The "e" variable in this code holds lots of data about the click event, including which material has been clicked, so, because we know which material is on each face, we can tell by the materials name which face has been clicked!

With a little bit of code, you can make a nice spinning cube gallery like the example above:

Here is my final code, have fun!

Actionscript:
  1. package  {
  2.     
  3.     import flash.display.DisplayObject;
  4.     import org.papervision3d.materials.BitmapFileMaterial;
  5.     import org.papervision3d.materials.utils.MaterialsList;
  6.     import org.papervision3d.events.InteractiveScene3DEvent;
  7.     import org.papervision3d.objects.primitives.Cube;
  8.     
  9.     public class Main extends PaperBase {
  10.       
  11.        private var frontMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/front.jpg");
  12.        private var backMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/back.jpg");
  13.        private var leftMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/left.jpg");
  14.        private var rightMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/right.jpg");
  15.        private var topMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/top.jpg");
  16.        private var bottomMaterial:BitmapFileMaterial = new BitmapFileMaterial("http://papervision2.com/wp-content/pvTex/bottom.jpg");
  17.       
  18.        private var targetrotationX:Number = 0;
  19.        private var targetrotationY:Number = 0;
  20.        private var targetrotationZ:Number = 0;
  21.       
  22.        private var tweening:Boolean = false;
  23.       
  24.        private var cube:Cube;
  25.       
  26.        public function Main() {
  27.          init(400, 400);
  28.        }
  29.       
  30.        override protected function init3d():void {
  31.          // We need to be able to identify each side. We'll do this
  32.          // by asssigning names to each material. During this process
  33.          // we'll also make the materials interactive.
  34.          frontMaterial.interactive = true;
  35.          frontMaterial.name = "front";
  36.          backMaterial.interactive = true;
  37.          backMaterial.name = "back";
  38.          leftMaterial.interactive = true;
  39.          leftMaterial.name = "left";
  40.          rightMaterial.interactive = true;
  41.          rightMaterial.name = "right";
  42.          topMaterial.interactive = true;
  43.          topMaterial.name = "top";
  44.          bottomMaterial.interactive = true;
  45.          bottomMaterial.name = "bottom";
  46.          // ---------------------------------------------
  47.          
  48.          cube = new Cube(new MaterialsList( {
  49.               front: frontMaterial,
  50.               back: backMaterial,
  51.               left: leftMaterial,
  52.               right: rightMaterial,
  53.               top: topMaterial,
  54.               bottom: bottomMaterial
  55.               } ), 500, 500, 500, 3, 3, 3);
  56.          // Listen for the click:
  57.          cube.addEventListener( InteractiveScene3DEvent.OBJECT_PRESS, onPress);
  58.          // Add to scene:
  59.          default_scene.addChild(cube);
  60.        }
  61.       
  62.        private function onPress( e:InteractiveScene3DEvent ):void {
  63.          // If the cube has been moved to the front:
  64.          if (tweening) {
  65.               // Let it rotate again
  66.               tweening = false;
  67.          }else {
  68.               // Find which rotation we need to be able to see
  69.               // the face image:
  70.               switch(e.face3d.material.name) {
  71.                   case "front":
  72.                      targetrotationX = 0;
  73.                      targetrotationY = 180;
  74.                      targetrotationZ = 0;
  75.                      tweening = true;
  76.                   break;
  77.                   case "back":
  78.                      targetrotationX = 0;
  79.                      targetrotationY = 0;
  80.                      targetrotationZ = 0;
  81.                      tweening = true;
  82.                   break;
  83.                   case "left":
  84.                      targetrotationX = 0;
  85.                      targetrotationY = -90;
  86.                      targetrotationZ = 0;
  87.                      tweening = true;
  88.                   break;
  89.                   case "right":
  90.                      targetrotationX = 0;
  91.                      targetrotationY = 90;
  92.                      targetrotationZ = 0;
  93.                      tweening = true;
  94.                   break;
  95.                   case "top":
  96.                      targetrotationX = 90;
  97.                      targetrotationY = 0;
  98.                      targetrotationZ = 0;
  99.                      tweening = true;
  100.                   break;
  101.                   case "bottom":
  102.                      targetrotationX = -90;
  103.                      targetrotationY = 0;
  104.                      targetrotationZ = 180;
  105.                      tweening = true;
  106.                   break;
  107.               }
  108.          }
  109.        }
  110.       
  111.        override protected function processFrame():void {
  112.          if (tweening) {
  113.               // If a face has been clicked
  114.               if (default_camera.zoom <6.8) {
  115.                   // If the camera isn't zoomed enough then zoom in a bit more:
  116.                   default_camera.zoom += Math.sqrt(6.8-default_camera.zoom)/5;
  117.               }
  118.              
  119.               // Test each rotation and rotate it towards the target rotation:
  120.               // X axis:
  121.               if (cube.rotationX <targetrotationX) {
  122.                   cube.rotationX += Math.sqrt(targetrotationX-cube.rotationX);
  123.                   cube.rotationX = Math.round(cube.rotationX);
  124.               }else if (cube.rotationX> targetrotationX) {
  125.                   cube.rotationX -= Math.sqrt(cube.rotationX-targetrotationX);
  126.                   cube.rotationX = Math.round(cube.rotationX);
  127.               }
  128.               // Y axis:
  129.               if (cube.rotationY <targetrotationY) {
  130.                   cube.rotationY += Math.sqrt(targetrotationY-cube.rotationY);
  131.                   cube.rotationY = Math.round(cube.rotationY);
  132.               }else if (cube.rotationY> targetrotationY) {
  133.                   cube.rotationY -= Math.sqrt(cube.rotationY-targetrotationY);
  134.                   cube.rotationY = Math.round(cube.rotationY);
  135.               }
  136.               // Z axis:
  137.               if (cube.rotationZ <targetrotationZ) {
  138.                   cube.rotationZ += Math.sqrt(targetrotationZ-cube.rotationZ);
  139.                   cube.rotationZ = Math.round(cube.rotationZ);
  140.               }else if (cube.rotationZ> targetrotationZ) {
  141.                   cube.rotationZ -= Math.sqrt(cube.rotationZ-targetrotationZ);
  142.                   cube.rotationZ = Math.round(cube.rotationZ);
  143.               }
  144.          }else {
  145.               // If the camera is zoomed in, it shouldn't be now
  146.               if (default_camera.zoom> 2) {
  147.                   // So zoom out a bit.
  148.                   default_camera.zoom -= Math.sqrt(default_camera.zoom-2)/5;
  149.               }
  150.              
  151.               // Rotate the cube a bit:
  152.               cube.rotationX += 2;
  153.               cube.rotationY += 2;
  154.              
  155.               // Make sure that we dont "wind up" the rotation
  156.               if (cube.rotationX>= 360) cube.rotationX = 0;
  157.               if (cube.rotationY>= 360) cube.rotationY = 0;
  158.          }
  159.        }
  160.     }
  161. }

37 Comments

  1. Comment by Mike on February 18, 2008 12:01 pm

    Hey Luke,

    this example is quite nice. But to load a complete movieClip (which contains buttons) into a pane (instead of a graphic) is not possible, is it? I would like just to use Buttons _in_ these movieClips to do sth. And not the pane it self...

    Hmm.. Perhaps if I know the coordinates of the buttons, I can create a matrix with x, y coordinates to find out which button is clicked. It is quite complicated. Do you see an other way?

    Mike

  2. Comment by Christian on February 19, 2008 12:11 am

    Hey ya,

    Mouse events are just not working for me, any idea?
    The cube rotes but does not react on me. This is the same for the basic interactivity demo.

    Christian

  3. Comment by Luke on February 19, 2008 12:18 am

    @Mike,

    I think there is a way which involves placing the movie clip over the viewport with a very low alpha level, then grabbing the coordinates of the mouse on the texture on mouse over, and moving the movieclip so that it's positioned underneath the mouse correctly. I'll try to find the example that I saw..

    @Christian,

    What version of Flash Player are you using? Does the interactivity work when you view the movie directly from your browser (Try navigating to http://papervision2.com/wp-content/advancedinteractive.swf)

    -Luke

  4. Comment by Mike on February 19, 2008 9:51 am

    Hi Luke,

    that sounds quite interesting. Would be really cool if you can post that example. Currently I am using a callback within the movieClips I load into the plane, which is called by init2d() and creates an exact copy of the items which should be clickable on the screen, when the plane of the cube is in view. It is working ok. But this mouse thing seems to be more elegant.

    I am looking forward to your post.

    Mike

  5. Comment by toojee on February 19, 2008 4:57 pm

    Hello,

    for use a movieclip i do :
    var clip:Myclip = new Myclip()
    var material:MovieMaterial = new MovieMaterial( clip, true, true )
    material.interactive = true
    material.oneSide = false
    material.smooth = true
    //----------------------------------------------------
    plane = new Plane( material, 250, 250, 10, 10 )
    clip.bouton.addEventListener( MouseEvent.ROLL_OVER, onOver );
    clip.bouton.addEventListener( MouseEvent.ROLL_OUT, onOut );
    scene.addChild( plane )

    But the rollOver on Button in Movieclip is not perfectly issue.
    Try it and tell me if its fine for you

    Ps: sorry for my language im french

  6. Comment by Luke on February 19, 2008 5:17 pm

    Hi toojee,

    I think I understand what you mean and I'll be showing you how to do this in my next tutorial!

    Thanks for visiting!

    -Luke

  7. Comment by toojee on February 20, 2008 8:57 am

    Thanks you Luke!
    With that, we will can do a website in 3d!

  8. Comment by michael on February 21, 2008 1:30 am

    I was wondering how I could make the the box slightly transparent. I know that it probably has something to do with the MaterialObject3D class but I can't figure it out. Is it even possible?
    The tutorials are really terrific! Thanks!

  9. Comment by adam on February 21, 2008 6:13 pm

    Hello,

    I'm trying to make buttons outside of the model to control the cube but nothing seems to be working can you take a look at it? I'm getting this error:
    1120: Access of undefined property e.

  10. Comment by adam on February 21, 2008 6:26 pm

    heres the code:

    b1.addEventListener('click', b1click);
    function b1click(event:Event):void {
    // If the cube has been moved to the front:
    if (tweening) {
    // Let it rotate again
    tweening = false;
    } else {
    // Find which rotation we need to be able to see
    // the face image:
    switch (e.face3d.material.name) {
    case "front" :
    targetrotationX = 0;
    targetrotationY = 180;
    targetrotationZ = 0;
    tweening = true;
    break;
    }
    }

  11. Comment by Luke on February 21, 2008 6:34 pm

    Hi Adam,

    Change the line

    Actionscript:
    1. switch (e.face3d.material.name) {

    to

    Actionscript:
    1. switch (event.face3d.material.name) {

    Hope this helps!

    -Luke

  12. Comment by adam on February 21, 2008 7:19 pm

    Thanks! for the response, but now I'm getting this error:
    1119: Access of possibly undefined property face3d through a reference with static type flash.events:Event.

  13. Comment by morphy on February 24, 2008 4:19 am

    Hello !

    Thank you for this website and for the tutorials.

    I'm trying to add interactivity on a simple cube, like you are doing in this tutorial, but I can't catch any event on my cube.
    Here is my code : (all the code in this class, I have no "PaperBase Class" like you)

    (let's try to use the CODE tag, you can edit my post if the tag is wrong)

    CODE:
    1. package
    2. {
    3.     import flash.display.Sprite;
    4.     import flash.events.Event;
    5.    
    6.    
    7.     import org.papervision3d.cameras.Camera3D;
    8.     import org.papervision3d.objects.primitives.Cube;
    9.     import org.papervision3d.scenes.Scene3D;
    10.     import org.papervision3d.materials.BitmapMaterial;
    11.     import org.papervision3d.materials.utils.MaterialsList;
    12.    
    13.     import org.papervision3d.events.InteractiveScene3DEvent;
    14.     import org.papervision3d.view.Viewport3D;
    15.     import org.papervision3d.render.BasicRenderEngine;
    16.    
    17.     public class TestCube extends Sprite
    18.     {
    19.        
    20.         public var viewport:Viewport3D;
    21.         public var renderer:BasicRenderEngine;
    22.         private var scene: Scene3D;
    23.         private var camera: Camera3D;
    24.         private var cube: Cube;
    25.  
    26.         public function TestCube()
    27.         {
    28.            
    29.             viewport = new Viewport3D(400, 400);
    30.             addChild( viewport );
    31.            
    32.             renderer = new BasicRenderEngine();
    33.            
    34.             // create a scene
    35.             scene = new Scene3D();
    36.            
    37.             // create a camera
    38.             camera = new Camera3D();
    39.             camera.z = -500;
    40.             camera.zoom = 5;
    41.  
    42.             // creation des textures à partir des bitmapdata en bibliothèque
    43.             var material1:BitmapMaterial = new BitmapMaterial( new bmpface1(300, 300) );
    44.             var material2:BitmapMaterial = new BitmapMaterial( new bmpface2(300, 300) );
    45.             var material3:BitmapMaterial = new BitmapMaterial( new bmpface3(300, 300) );
    46.             var material4:BitmapMaterial = new BitmapMaterial( new bmpface4(300, 300) );
    47.             var material5:BitmapMaterial = new BitmapMaterial( new bmpface5(300, 300) );
    48.             var material6:BitmapMaterial = new BitmapMaterial( new bmpface6(300, 300) );
    49.            
    50.             // on rend les textures interactives
    51.            
    52.             material1.interactive = true;
    53.             material2.interactive = true;
    54.             material3.interactive = true;