Relative camera movement code help

I want to use the Standard mobile asset camera code for my player movement. In the original code, when the player is 90 degree, if I change the direction to opposite, the player is not rotate to the opposite direction, it just flip to opposite.

What I don’t understand is how to determine the player direction when I move the joystick or press the keyboard, and make the player rotate to the opposite direction instead of flip.

Here is the Camera relative code in Standard Mobile Asset, I replace the joystick input to keyboard for testing. Any help would be appreciate.

//////////////////////////////////////////////////////////////
// Joystick.js
// Penelope iPhone Tutorial
//
// Joystick creates a movable joystick (via GUITexture) that 
// handles touch input, taps, and phases. Dead zones can control
// where the joystick input gets picked up and can be normalized.
//
// Optionally, you can enable the touchPad property from the editor
// to treat this Joystick as a TouchPad. A TouchPad allows the finger
// to touch down at any point and it tracks the movement relatively 
// without moving the graphic
//////////////////////////////////////////////////////////////

@script RequireComponent( GUITexture )

// A simple class for bounding how far the GUITexture will move
class Boundary 
{
	var min : Vector2 = Vector2.zero;
	var max : Vector2 = Vector2.zero;
}

static private var joysticks : Joystick[];					// A static collection of all joysticks
static private var enumeratedJoysticks : boolean = false;
static private var tapTimeDelta : float = 0.3;				// Time allowed between taps

var touchPad : boolean; 									// Is this a TouchPad?
var touchZone : Rect;
var deadZone : Vector2 = Vector2.zero;						// Control when position is output
var normalize : boolean = false; 							// Normalize output after the dead-zone?
var position : Vector2; 									// [-1, 1] in x,y
var tapCount : int;											// Current tap count

private var lastFingerId = -1;								// Finger last used for this joystick
private var tapTimeWindow : float;							// How much time there is left for a tap to occur
private var fingerDownPos : Vector2;
private var fingerDownTime : float;
private var firstDeltaTime : float = 0.5;

private var gui : GUITexture;								// Joystick graphic
private var defaultRect : Rect;								// Default position / extents of the joystick graphic
private var guiBoundary : Boundary = Boundary();			// Boundary for joystick graphic
private var guiTouchOffset : Vector2;						// Offset to apply to touch input
private var guiCenter : Vector2;							// Center of joystick

function Start()
{
	// Cache this component at startup instead of looking up every frame	
	gui = GetComponent( GUITexture );
	
	// Store the default rect for the gui, so we can snap back to it
	defaultRect = gui.pixelInset;	
    
    defaultRect.x += transform.position.x * Screen.width;// + gui.pixelInset.x; // -  Screen.width * 0.5;
    defaultRect.y += transform.position.y * Screen.height;// - Screen.height * 0.5;
    
    transform.position.x = 0.0;
    transform.position.y = 0.0;
        
	if ( touchPad )
	{
		// If a texture has been assigned, then use the rect ferom the gui as our touchZone
		if ( gui.texture )
			touchZone = defaultRect;
	}
	else
	{				
		// This is an offset for touch input to match with the top left
		// corner of the GUI
		guiTouchOffset.x = defaultRect.width * 0.5;
		guiTouchOffset.y = defaultRect.height * 0.5;
		
		// Cache the center of the GUI, since it doesn't change
		guiCenter.x = defaultRect.x + guiTouchOffset.x;
		guiCenter.y = defaultRect.y + guiTouchOffset.y;
		
		// Let's build the GUI boundary, so we can clamp joystick movement
		guiBoundary.min.x = defaultRect.x - guiTouchOffset.x;
		guiBoundary.max.x = defaultRect.x + guiTouchOffset.x;
		guiBoundary.min.y = defaultRect.y - guiTouchOffset.y;
		guiBoundary.max.y = defaultRect.y + guiTouchOffset.y;
	}
}

function Disable()
{
	gameObject.active = false;
	enumeratedJoysticks = false;
}

function ResetJoystick()
{
	// Release the finger control and set the joystick back to the default position
	gui.pixelInset = defaultRect;
	lastFingerId = -1;
	position = Vector2.zero;
	fingerDownPosition = Vector2.zero;
	
	if ( touchPad )
		gui.color.a = 0.025;	
}

function IsFingerDown() : boolean
{
	return (lastFingerId != -1);
}
	
function LatchedFinger( fingerId : int )
{
	// If another joystick has latched this finger, then we must release it
	if ( lastFingerId == fingerId )
		ResetJoystick();
}

function Update()
{	
	if ( !enumeratedJoysticks )
	{
		// Collect all joysticks in the game, so we can relay finger latching messages
		joysticks = FindObjectsOfType( Joystick );
		enumeratedJoysticks = true;
	}	
		
	var count = Input.touchCount;
	
	// Adjust the tap time window while it still available
	if ( tapTimeWindow > 0 )
		tapTimeWindow -= Time.deltaTime;
	else
		tapCount = 0;
	
	if ( count == 0 )
		ResetJoystick();
	else
	{
		for(var i : int = 0;i < count; i++)
		{
			var touch : Touch = Input.GetTouch(i);			
			var guiTouchPos : Vector2 = touch.position - guiTouchOffset;
	
			var shouldLatchFinger = false;
			if ( touchPad )
			{				
				if ( touchZone.Contains( touch.position ) )
					shouldLatchFinger = true;
			}
			else if ( gui.HitTest( touch.position ) )
			{
				shouldLatchFinger = true;
			}		
	
			// Latch the finger if this is a new touch
			if ( shouldLatchFinger  ( lastFingerId == -1 || lastFingerId != touch.fingerId ) )
			{
				
				if ( touchPad )
				{
					gui.color.a = 0.15;
					
					lastFingerId = touch.fingerId;
					fingerDownPos = touch.position;
					fingerDownTime = Time.time;
				}
				
				lastFingerId = touch.fingerId;
				
				// Accumulate taps if it is within the time window
				if ( tapTimeWindow > 0 )
					tapCount++;
				else
				{
					tapCount = 1;
					tapTimeWindow = tapTimeDelta;
				}
											
				// Tell other joysticks we've latched this finger
				for ( var j : Joystick in joysticks )
				{
					if ( j != this )
						j.LatchedFinger( touch.fingerId );
				}						
			}				
	
			if ( lastFingerId == touch.fingerId )
			{	
				// Override the tap count with what the iPhone SDK reports if it is greater
				// This is a workaround, since the iPhone SDK does not currently track taps
				// for multiple touches
				if ( touch.tapCount > tapCount )
					tapCount = touch.tapCount;
				
				if ( touchPad )
				{	
					// For a touchpad, let's just set the position directly based on distance from initial touchdown
					position.x = Mathf.Clamp( ( touch.position.x - fingerDownPos.x ) / ( touchZone.width / 2 ), -1, 1 );
					position.y = Mathf.Clamp( ( touch.position.y - fingerDownPos.y ) / ( touchZone.height / 2 ), -1, 1 );
				}
				else
				{					
					// Change the location of the joystick graphic to match where the touch is
					gui.pixelInset.x =  Mathf.Clamp( guiTouchPos.x, guiBoundary.min.x, guiBoundary.max.x );
					gui.pixelInset.y =  Mathf.Clamp( guiTouchPos.y, guiBoundary.min.y, guiBoundary.max.y );		
				}
				
				if ( touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled )
					ResetJoystick();					
			}			
		}
	}
	
	if ( !touchPad )
	{
		// Get a value between -1 and 1 based on the joystick graphic location
		position.x = ( gui.pixelInset.x + guiTouchOffset.x - guiCenter.x ) / guiTouchOffset.x;
		position.y = ( gui.pixelInset.y + guiTouchOffset.y - guiCenter.y ) / guiTouchOffset.y;
	}
	
	// Adjust for dead zone	
	var absoluteX = Mathf.Abs( position.x );
	var absoluteY = Mathf.Abs( position.y );
	
	if ( absoluteX < deadZone.x )
	{
		// Report the joystick as being at the center if it is within the dead zone
		position.x = 0;
	}
	else if ( normalize )
	{
		// Rescale the output after taking the dead zone into account
		position.x = Mathf.Sign( position.x ) * ( absoluteX - deadZone.x ) / ( 1 - deadZone.x );
	}
		
	if ( absoluteY < deadZone.y )
	{
		// Report the joystick as being at the center if it is within the dead zone
		position.y = 0;
	}
	else if ( normalize )
	{
		// Rescale the output after taking the dead zone into account
		position.y = Mathf.Sign( position.y ) * ( absoluteY - deadZone.y ) / ( 1 - deadZone.y );
	}
}

I tried to replace the following code so the player character rotate to the target direction smoothly, but fail.

What I can understood from the original code is:
The movement was set by the TransformDirection from keyboard input;
When the character move, it has velocity (correct me if I am wrong);
Set the player character’s transform.forward to the velocity, the player “rotate” to the velocity vector;

I think the movement store the direction vector, I tried use the follow modified code, however, it didn’t ran as expected.

function FaceMovementDirection()
{	
	horizontalVelocity = character.velocity;
	horizontalVelocity.y = 0; // Ignore vertical movement

	Debug.Log(horizontalVelocity.magnitude);
	
	// If moving significantly in a new direction, point that character in that direction
	//if ( horizontalVelocity.magnitude > 0.1 )
		//thisTransform.forward = horizontalVelocity.normalized;

        // Smoothly rotate to new direction
	if ( horizontalVelocity.magnitude > 0.1 ) {
	
		// Determine the target rotation.  This is the rotation if the transform looks at the target direction.
		var targetRotation = Quaternion.LookRotation(movement - thisTransform.position);
		
		// Smoothly rotate the object towards the target point.
		thisTransform.rotation = Quaternion.Lerp(thisTransform.rotation, targetRotation, 10 * Time.deltaTime);
		thisTransform.localEulerAngles = new Vector3(0, thisTransform.localEulerAngles.y, 0);
	}		

}

I’m having a similar issue. In the camera relative setup, Penelope doesn’t seem to turn smoothly, the character just snaps to whatever direction the joystick is, looks unpolished. Bumping this maybe someone can point us in the right direction

Why set the camera’s rotation twice, using two different methods? … and it looks like it is referencing itself at the same time…

thisTransform.rotation = Quaternion.Lerp(thisTransform.rotation, targetRotation, 10 * Time.deltaTime);
thisTransform.localEulerAngles = new Vector3(0, thisTransform.localEulerAngles.y, 0);

I think you want something more like:

Quaternion newRot = Quaternion.Lerp(thisTransform.rotation, targetRotation, 10 * Time.deltaTime);         
thisTransform.localEulerAngles = new Vector3(0, newRot.[URL="http://unity3d.com/support/documentation/ScriptReference/Quaternion-eulerAngles.html"]eulerAngles[/URL].y, 0);

I didn’t test this, but at least it is on the right track. I notice you are using rotation instead of localRotation in there then setting to a localRotation. That might be a place that needs some work.

EDIT: Ahh I think I see why the original code was written that way. Could work, but I hate setting anything twice, even just to cast from global to local space. I’m guessing you are getting a working SLERP but the “y axis” is wonky? Please provide more detail regarding what you are seeing.

Actually I found the ShiVa 3rd person mobile control is much better, the player character turn smoothly, and the virtual joystick is moving in a circle, not rectangle like the virtual joystick in the mobile asset. I think it is the time for Unity Mobile Asset team update the code and the example.