Warning: session_start() [function.session-start]: open(/tmp/sess_t5i3tjih9cup2u8i0trcpj05h4, O_RDWR) failed: No space left on device (28) in /home/autres/sandy/site/wiki/inc/init.php on line 103

Warning: session_write_close() [function.session-write-close]: open(/tmp/sess_t5i3tjih9cup2u8i0trcpj05h4, O_RDWR) failed: No space left on device (28) in /home/autres/sandy/site/wiki/doku.php on line 75

Warning: session_write_close() [function.session-write-close]: Failed to write session data (files). Please verify that the current setting of session.save_path is correct () in /home/autres/sandy/site/wiki/doku.php on line 75
Sandy 3D engine (AS3 & AS2) for Adobe Flash/tutorials/3.0/sandy_cs3_tut073

Author: Max Pellizzaro
Date: February 24th 2008
Update: July 12th 2009
version: 3.1.1

3DS MAX moving around the camero: Part III

Objective of the tutorial

In this tutorial, you will learn how to make the Camero move around, turning and backing up. In order to understand how this tutorial works, you should have completed the previous two that taught you, How to Import the Car and how to Control the Movement of the Wheels.

I’m sure you will have fun in learning how to really animate the movement of the Camero, since I had so much fun in preparing this tutorial.

How to

Set up the 3DMAX model

Have a look at the

Set up

The Document class must be changed to Example0073.as The name of the class in the .as file and the name of the constructor now is: Example0073.

example073.rar

The new updated version can be found here:
example0073_v3.1.1.zip

The AS Code

package
{
   import flash.display.*; 
   import flash.events.*;
   import flash.ui.*;
   import flash.net.URLRequest;
   
   import sandy.core.Scene3D;
   import sandy.core.data.*;
   import sandy.core.scenegraph.*;
   import sandy.materials.*;
   import sandy.materials.attributes.*;
   import sandy.primitive.*;
   import sandy.parser.*;
   import sandy.util.*;
   import sandy.events.*;
   import sandy.view.*;

   public class Example0073 extends Sprite 
   {
      private var scene:Scene3D;
	  private var camera:Camera3D;
	  private var tg:TransformGroup;
	  private var tg2:TransformGroup;
	  private var car:Shape3D;
	  private var wheelLF:Shape3D;
	  private var wheelRF:Shape3D;
	  private var wheelLR:Shape3D;
	  private var wheelRR:Shape3D;
	  private var queue:LoaderQueue;
      private var parserStack:ParserStack;
	  private var speed:Number = 0;
	  private var angle:Number = 0;
	  
      public function Example0073()
      { 
		var parser:IParser = Parser.create("assets/models/ASE/car.ASE",Parser.ASE );
		var parserLF:IParser = Parser.create("assets/models/ASE/wheel_Front_L.ASE",Parser.ASE );
	    var parserRF:IParser = Parser.create("assets/models/ASE/wheel_Front_R.ASE",Parser.ASE );
	    var parserLR:IParser = Parser.create("assets/models/ASE/wheel_Rear_L.ASE",Parser.ASE );
	    var parserRR:IParser = Parser.create("assets/models/ASE/wheel_Rear_R.ASE",Parser.ASE );
	    
		parserStack = new ParserStack();
		parserStack.add("carParser",parser);
		parserStack.add("wheelLFParser",parserLF);
		parserStack.add("wheelRFParser",parserRF);
		parserStack.add("wheelLRParser",parserLR);
		parserStack.add("wheelRRParser",parserRR);
		parserStack.addEventListener(ParserStack.COMPLETE, parserComplete );
		parserStack.start();
		
	  }
	  
	  private function parserComplete(pEvt:Event ):void
      {
		car = parserStack.getGroupByName("carParser").children[0] as Shape3D;
		wheelLF = parserStack.getGroupByName("wheelLFParser").children[0] as Shape3D;
	    wheelRF = parserStack.getGroupByName("wheelRFParser").children[0] as Shape3D;
		wheelLR = parserStack.getGroupByName("wheelLRParser").children[0] as Shape3D;
		wheelRR = parserStack.getGroupByName("wheelRRParser").children[0] as Shape3D;
		loadSkins();
	  }
	  
	  
	  private function loadSkins(){
	   queue = new LoaderQueue();
       queue.add( "carSkin", new URLRequest("assets/textures/car.jpg") );
	   queue.add( "wheels", new URLRequest("assets/textures/wheel.jpg") );
	   
	   queue.addEventListener(SandyEvent.QUEUE_COMPLETE, loadComplete );
       queue.start();
	  }
      
	  // Create the scene graph based on the root Group of the scene
      private function loadComplete(event:QueueEvent)
      {
         camera = new Camera3D( 700, 370 );
		 camera.y = 50;
		 camera.z = -100;
		 camera.near = 10;
		 camera.viewport = new ViewPort(700,200);
		 var root:Group = createScene();
		 
		 var canvas:Sprite = new Sprite();
		 this.addChild(canvas);
		 scene = new Scene3D( "scene", canvas, camera, root );
		 
		 // Listen to the heart beat and render the scene
         addEventListener( Event.ENTER_FRAME, enterFrameHandler );
		 stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressedHandler);
      }
	  
	  private function createScene():Group
      {
         // Create the root Group
         var g:Group = new Group();
		 
		 // We need to create a transformGroup that will group the chassis and the wheels
		 tg = new TransformGroup('myGroup');
		 
		 // we need a second transformGroup to be able to rotate the car other than its center
		 tg2 = new TransformGroup();
		  
		 var material:BitmapMaterial = new BitmapMaterial( queue.data["carSkin"].bitmapData );
		 var app:Appearance = new Appearance( material );
		 car.appearance = app;
		 
		 var materialW:BitmapMaterial = new BitmapMaterial( queue.data["wheels"].bitmapData );
		 var appW:Appearance = new Appearance( materialW );
		 wheelLF.appearance = appW;
		 wheelRF.appearance = appW;
		 wheelLR.appearance = appW;
		 wheelRR.appearance = appW;
		 
		 // use single container = False
		 car.useSingleContainer = false;
		 wheelLF.useSingleContainer = false;
		 wheelRF.useSingleContainer = false;
		 wheelLR.useSingleContainer = false;
		 wheelRR.useSingleContainer = false;
		 
		 // change  geometry center for wheels
		 wheelRF.geometryCenter = new Point3D(-24,-11.5,-48.5);
		 wheelRF.x += 24;
		 wheelRF.y += 11.5;
		 wheelRF.z += 48.5;
		 
		 wheelLF.geometryCenter = new Point3D(24.5,-11.5,-48.5);
		 wheelLF.x -= 24.5;
		 wheelLF.y += 11.5;
		 wheelLF.z += 48.5;
		 
		 wheelRR.geometryCenter = new Point3D(-24,-11.5,+41);
		 wheelRR.x += 24;
		 wheelRR.y += 11.5;
		 wheelRR.z -= 41;
		 
		 wheelLR.geometryCenter = new Point3D(24.5,-11.5,+41);
		 wheelLR.x -= 24.5;
		 wheelLR.y += 11.5;
		 wheelLR.z -= 41;
		 
		 car.geometryCenter = new Point3D(0,0,+41);
		 car.z -= 41;
		 
		 tg.addChild( wheelRF );
		 tg.addChild( wheelLF );
		 tg.addChild( wheelRR );
		 tg.addChild( wheelLR );
		 tg.addChild( car );
		 
		 // set the offset
		 tg.z += 50;
		 tg2.addChild(tg);
		 
		 tg2.x = -50;
		 tg2.y = 0;
		 tg2.z = 150;
		 
		 tg2.rotateY = -130;
		 
		 g.addChild( tg2 );
		 return g;
      }

	  private function keyPressedHandler(event:flash.events.KeyboardEvent):void 
	  {
	   switch(event.keyCode) {
		case Keyboard.SPACE:
			tg2.x = -50;
			tg2.y = 0;
			tg2.z = 150;
			break;
		case Keyboard.UP:
			if (speed < 6)
			 speed += 1;
			break;
		case Keyboard.DOWN:
			if (speed > -4)
			 speed -= 1;
			break;
		case Keyboard.LEFT:
		    if(wheelLF.pan>=-40){
			 wheelLF.tilt =0;
			 wheelRF.tilt =0;
			 wheelLF.pan -=4;
			 wheelRF.pan -=4;
			}
			break;
		case Keyboard.RIGHT:
		    if(wheelLF.pan<=40){
		     wheelLF.tilt =0;
			 wheelRF.tilt =0;
			 wheelLF.pan +=4;
			 wheelRF.pan +=4;
			}
			break;
		}
	  }
	  // The Event.ENTER_FRAME event handler tells the world to render
      private function enterFrameHandler( event : Event ) : void
      {
        //camera.setPerspectiveProjection( 80, 2, 10, 10000 );
		
		tg2.moveForward(speed);
		wheelLF.tilt +=4*speed;
	    wheelRF.tilt +=4*speed;
		wheelRR.tilt +=4*speed;
		wheelLR.tilt +=4*speed;
		if (speed>0)
		 tg2.pan +=wheelLF.pan/20;
		else if (speed < 0)
		 tg2.pan -=wheelLF.pan/20;
		
		//if(tg2.z < -100){
		//  tg2.visible = false;
		//} else if (tg2.z > -100) {
		//  tg2.visible = true;
		//}
		scene.render();
      }
   }
}

Examining the code

I would like to present the steps I did in order to enrich tutorial Part II and make it become tutorial Past III, because I’m sure there is a lot to learn.
First of all I want to make the car turning, so my first try was to just rotate my Transformation Group “tg” with something like this:

 
case Keyboard.LEFT:
 …
  tg.pan -=2;
 …
  break;
case Keyboard.LEFT:
 …
  tg.pan +=2;
 …
  break;

But the result did not satisfy me. When running this code my car was rotating against its central axis, and I did not like it at all. If you think how the car turns in reality, the center of rotation is on the back of the car, not in the center of the car. So in order to make a more realistic move we need to move the axis of rotation from the middle of the car to the back, as shown in the next picture.

Now since the car it is not a single “geometry”, we cannot use the property geometryCenter as we did for each single wheel. But we can use TransformationGroup! The idea is this one:

Well it is more difficult to explain all that than to write it, here is the code:

 tg = new TransformGroup('myGroup');
 tg.addChild( wheelRF );
 tg.addChild( wheelLF );
 tg.addChild( wheelRR );
 tg.addChild( wheelLR );
 tg.addChild( car );
 
// set the offset
 tg.z += 50;
 
tg2 = new TransformGroup();		 
tg2.addChild(tg);

Now we are ready to make the car move!
In the previous tutorial all the controls where inside the keyPressedHandler(..) function, which is still a good place to make the control of the steering wheels, but not to control the movement of the car. We will place the code that moves the car inside the enterFrameHandler(..) function.
Here is the code:

private function enterFrameHandler( event : Event ) : void
{
     tg2.moveForward(speed);
     wheelLF.tilt +=4*speed;
     wheelRF.tilt +=4*speed;
     wheelRR.tilt +=4*speed;
     wheelLR.tilt +=4*speed;
     if (speed>0)
       tg2.pan +=wheelLF.pan/20;
     else if (speed < 0)
       tg2.pan -=wheelLF.pan/20;
 
  scene.render();
      }

The speed variable is controlled and set inside the keyPressedHandler(..) function, as the name suggests it controls the speed of the car.
In this function you can clearly see that we are controlling three distinct movements:

Now we are close to an end, but let’s make one more little adjustment.
If you remember I have decide to put as the background of the entire scene a static picture. It represents a desert with a specific perspective, or view point. Now you can bet that the default prospective of Sandy will never match any picture you might choose. If you try the code without touching anything about perspective you will notice that moving the car away, increasing z-value, visually it will look like the car is diving into the ground.
But Sandy gives you some elements to adjust the perspective, and I will use the camera viewport property, and will set it like this:

camera.viewport = new ViewPort(700,200);

Now we are done, and it's time to see the result!

The output

Have fun!