Yes and no. I have it fixed so it does snap behind the player when using the Right Mouse button and when using the left it will Rotate Around the player.
But I am having issues with the collision detection on the camera. Currently if there is a break in the ray (I shoot the ray FROM the player to the origin) and I try to move the camera to that point. What sometimes happens is that it gets stuck. Trying to find help on that.
Let me post my code.
// This camera is similar to the one used in Jak Dexter
var target : Transform;
var distance = 4.0;
var height = 1.0;
var smoothLag = 0.2;
var maxSpeed = 10.0;
var snapLag = 0.3;
var clampHeadPositionScreenSpace = 0.75;
var lineOfSightMask : LayerMask = 0;
var zoomSpeed = 50;
private var isSnapping = false;
private var headOffset = Vector3.zero;
private var centerOffset = Vector3.zero;
private var controller : ThirdPersonController;
private var velocity = Vector3.zero;
private var targetHeight = 100000.0;
private var theta;
private var theta2;
private var isColliding = false;
var targetOffset = Vector3.zero;
var closerRadius : float = 0.2;
var closerSnapLag : float = 0.2;
var xSpeed = 200.0;
var ySpeed = 80.0;
var yMinLimit = -20;
var yMaxLimit = 80;
private var currentDistance = 10.0;
private var x = 0.0;
private var y = 0.0;
private var distanceVelocity = 0.0;
function Start () {
var angles = target.transform.eulerAngles;
x = angles.y;
y = angles.x;
currentDistance = distance;
// Make the rigid body not change rotation
if (rigidbody)
rigidbody.freezeRotation = true;
}
function Awake ()
{
theta = 0;
theta2 = 0;
var characterController : CharacterController = target.collider;
if (characterController)
{
centerOffset = characterController.bounds.center -
target.position;
headOffset = centerOffset;
headOffset.y = characterController.bounds.max.y -
target.position.y;
}
if (target)
{
controller = target.GetComponent(ThirdPersonController);
}
if (!controller)
Debug.Log("Please assign a target to the camera that has a Third Person Controller script component.");
}
function LateUpdate () {
var targetCenter = target.position + centerOffset;
var targetHead = target.position + headOffset;
// When jumping don't move camera upwards but only down!
if (controller.IsJumping ())
{
// We'd be moving the camera upwards, do that only if it's really high
var newTargetHeight = targetCenter.y + height;
if (newTargetHeight < targetHeight || newTargetHeight - targetHeight > 5)
targetHeight = targetCenter.y + height;
}
// When walking always update the target height
else
{
targetHeight = targetCenter.y + height;
}
// We start snapping when user pressed Fire2!
if (Input.GetButton("Fire2") !isSnapping)
{
//velocity = Vector3.zero;
isSnapping = true;
}
if (isSnapping)
{
//ApplySnapping (targetCenter);
OrbitPlayerFromBehind (targetCenter);
}
else
{
//ApplyPositionDamping (Vector3(targetCenter.x, targetHeight, targetCenter.z));
}
SetUpRotation(targetCenter, targetHead);
if (Input.GetMouseButton(1))
{
OrbitPlayerFromBehind (targetCenter);
}
if (Input.GetMouseButton(0))
{
OrbitPlayer (targetCenter);
isSnapping = false;
}
else
{
//ApplyPositionDamping (Vector3(targetCenter.x, targetHeight, targetCenter.z));
isSnapping = true;
}
distance += -(Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime) * zoomSpeed * Mathf.Abs(distance);
if (distance < 5)
{
distance = 5;
}
if (distance > 50)
{
distance = 50;
}
}
function ApplySnapping (targetCenter : Vector3)
{
}
function OrbitPlayer (targetPlayer : Vector3)
{
y = transform.position.y;
x += Input.GetAxis("Mouse X") * xSpeed * 0.02;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02;
var rotation = Quaternion.Euler(0, x, 0);
var targetPos = target.position;
var direction = rotation * -Vector3.forward;
var targetDistance = AdjustLineOfSight(targetPos, direction);
currentDistance = distance;
transform.rotation = rotation;
transform.position = targetPos + direction * currentDistance;
if (y - target.position.y > 50)
{
y = target.position.y + 50;
}
transform.position.y = y;
camera.transform.LookAt(targetPlayer);
}
function OrbitPlayerFromBehind (targetPlayer : Vector3)
{
var newPosition = Vector3();
var fwd = new Vector3();
var rightVector = new Vector3();
var upVector = new Vector3();
var movingVector = new Vector3();
var collisionVector = new Vector3();
var zeroVector = new Vector3();
if (Input.GetMouseButton(1))
{
var mouseY = Input.GetAxis("Mouse Y");
}
theta = jangle(transform.position - targetPlayer) * Time.deltaTime;
theta2 = theta2 + mouseY * Time.deltaTime;
if (1.40 <= theta2)
theta2 = 1.40;
fwd = target.TransformDirection(Vector3.forward);
fwd = fwd.normalized;
newPosition = target.position - (distance * fwd);
newPosition.y = target.position.y + distance * Mathf.Sin(theta2);
y = target.transform.eulerAngles.z;
x = target.transform.eulerAngles.y;
rightVector = Vector3(fwd.z, 0, -fwd.x);
rightVector = rightVector.normalized;
upVector = Vector3.Cross(fwd, rightVector);
upVector = upVector.normalized;
movingVector = Vector3.Slerp(transform.position, newPosition, Time.deltaTime * 50);
collisionVector = AdjustLineOfSight(transform.position, targetPlayer);
if (collisionVector != Vector3.zero)
{
Debug.Log("I am trying to get new position");
movingVector = collisionVector - (fwd * -1);
distance = movingVector.z - collisionVector.z;
}
transform.position = movingVector;
camera.transform.LookAt(targetPlayer, upVector);
}
function AdjustLineOfSight (newPosition : Vector3, target : Vector3)
{
var hit : RaycastHit;
if (Physics.Linecast (target, newPosition, hit, lineOfSightMask.value))
{
Debug.Log("I hit someting at: "+hit.point);
velocity = Vector3.zero;
isColliding = true;
return hit.point;
}
//return newPosition;
}
function ApplyPositionDamping (targetCenter : Vector3)
{
}
function SetUpRotation (centerPos : Vector3, headPos : Vector3)
{
}
function AngleDistance (a : float, b : float)
{
a = Mathf.Repeat(a, 360);
b = Mathf.Repeat(b, 360);
return Mathf.Abs(b - a);
}
function jangle(pTemp : Vector3)
{
var tTheta:Number;
pTemp.Normalize();
// quadrant I II
tTheta = Mathf.Acos(pTemp.x);
// quadtrant III IV
if (pTemp.z < 0)
tTheta = 2*Mathf.PI - tTheta;
return tTheta;
}
function bindAngle(tDegrees:Number)
{
if (tDegrees < 0)
tDegrees += Mathf.PI*2;
else if (Mathf.PI*2 <= tDegrees)
tDegrees += - Mathf.PI*2;
return tDegrees;
}
static function ClampAngle (angle : float, min : float, max : float) {
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp (angle, min, max);
}
@script AddComponentMenu ("Third Person Camera/Spring Follow Camera")
and this:
// The speed when walking
var walkSpeed = 3.0;
// after trotAfterSeconds of walking we trot with trotSpeed
var trotSpeed = 4.0;
// when pressing "Fire3" button (cmd) we start running
var runSpeed = 6.0;
var inAirControlAcceleration = 3.0;
// How high do we jump when pressing jump and letting go immediately
var jumpHeight = 0.5;
// We add extraJumpHeight meters on top when holding the button down longer while jumping
var extraJumpHeight = 2.5;
// The gravity for the character
var gravity = 20.0;
// The gravity in controlled descent mode
var controlledDescentGravity = 2.0;
var speedSmoothing = 10.0;
var rotateSpeed = 500.0;
var trotAfterSeconds = 3.0;
var canJump = true;
var canControlDescent = true;
var canWallJump = false;
private var jumpRepeatTime = 0.05;
private var wallJumpTimeout = 0.15;
private var jumpTimeout = 0.15;
private var groundedTimeout = 0.25;
// The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
private var lockCameraTimer = 0.0;
// The current move direction in x-z
private var moveDirection = Vector3.zero;
// The current vertical speed
private var verticalSpeed = 0.0;
// The current x-z move speed
private var moveSpeed = 0.0;
// The last collision flags returned from controller.Move
private var collisionFlags : CollisionFlags;
// Are we jumping? (Initiated with jump button and not grounded yet)
private var jumping = false;
private var jumpingReachedApex = false;
// Are we moving backwards (This locks the camera to not do a 180 degree spin)
private var movingBack = false;
// Is the user pressing any keys?
private var isMoving = false;
// When did the user start walking (Used for going into trot after a while)
private var walkTimeStart = 0.0;
// Last time the jump button was clicked down
private var lastJumpButtonTime = -10.0;
// Last time we performed a jump
private var lastJumpTime = -1.0;
// Average normal of the last touched geometry
private var wallJumpContactNormal : Vector3;
private var wallJumpContactNormalHeight : float;
// the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
private var lastJumpStartHeight = 0.0;
// When did we touch the wall the first time during this jump (Used for wall jumping)
private var touchWallJumpTime = -1.0;
private var inAirVelocity = Vector3.zero;
private var lastGroundedTime = 0.0;
private var lean = 0.0;
private var slammed = false;
private var isControllable = true;
private var oldForwardVector : Vector3;
private var strafe : float;
function Awake ()
{
moveDirection = transform.TransformDirection(Vector3.forward);
}
// This next function responds to the "HidePlayer" message by hiding the player.
// The message is also 'replied to' by identically-named functions in the collision-handling scripts.
// - Used by the LevelStatus script when the level completed animation is triggered.
function HidePlayer()
{
GameObject.Find("rootJoint").GetComponent(SkinnedMeshRenderer).enabled = false; // stop rendering the player.
isControllable = false; // disable player controls.
}
// This is a complementary function to the above. We don't use it in the tutorial, but it's included for
// the sake of completeness. (I like orthogonal APIs; so sue me!)
function ShowPlayer()
{
GameObject.Find("rootJoint").GetComponent(SkinnedMeshRenderer).enabled = true; // start rendering the player again.
isControllable = true; // allow player to control the character again.
}
function UpdateSmoothedMovementDirection ()
{
strafe = 0;
var cameraTransform = this.transform;
var grounded = IsGrounded();
// Forward vector relative to the camera along the x-z plane
var forward = cameraTransform.TransformDirection(Vector3.forward);
forward = forward.normalized;
// Right vector relative to the camera
// Always orthogonal to the forward vector
var right = Vector3(forward.z, 0, -forward.x);
right = right.normalized;
var v = Input.GetAxisRaw("Vertical");
var h = Input.GetAxisRaw("Horizontal");
// Are we moving backwards or looking backwards
if (v < -0.2)
movingBack = true;
else
movingBack = false;
var wasMoving = isMoving;
isMoving = Mathf.Abs (h) > 0.1 || Mathf.Abs (v) > 0.1;
// Target direction relative to the camera
var targetDirection = h * right + Mathf.Abs(v) * forward;
// Grounded controls
if (grounded)
{
// Lock camera for short period when transitioning moving standing still
//lockCameraTimer += Time.deltaTime;
// We store speed and direction seperately,
// so that when the character stands still we still have a valid forward direction
// moveDirection is always normalized, and we only update it if there is user input.
if (targetDirection != Vector3.zero)
{
// If we are really slow, just snap to the target direction
if (moveSpeed < walkSpeed * 0.9 grounded)
{
moveDirection = moveDirection.normalized;
}
// Otherwise smoothly turn towards it
else
{
moveDirection = moveDirection.normalized;
}
}
// Smooth the speed based on the current target direction
var curSmooth = speedSmoothing * Time.deltaTime;
// Choose target speed
// We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
var targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0);
// Pick speed modifier
if (Input.GetButton ("Fire3"))
{
targetSpeed *= runSpeed;
}
else if (Time.time - trotAfterSeconds > walkTimeStart)
{
//targetSpeed *= trotSpeed;
}
else
{
targetSpeed *= walkSpeed;
}
//Debug.Log("moveSpeed: "+moveSpeed);
if (v == 0 h != 0)
{
moveSpeed = .2;
}
else
{
moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
}
// Reset walk time start when we slow down
if (moveSpeed < walkSpeed * 0.3)
walkTimeStart = Time.time;
if (Input.GetAxis("Horizontal") != 0)
{
h = Input.GetAxis("Horizontal");
forward = transform.TransformDirection(Vector3.forward);
forward = forward.normalized;
right = Vector3(forward.z, 0, -forward.x);
right = right.normalized;
targetDirection = h * right + 10 * forward;
moveDirection = targetDirection;
moveDirection = moveDirection.normalized;
}
if (Input.GetMouseButton(1))
{
// Get the difference in horizontal mouse movement
h = Input.GetAxis("Mouse X");
if (h > 4)
{
h = 4;
}
if (h < -4)
{
h = -4;
}
forward = transform.TransformDirection(Vector3.forward);
forward = forward.normalized;
if (forward != oldForwardVector v == 0)
{
//Debug.Log("1 v: "+moveSpeed);
moveSpeed = 0.2;
}
right = Vector3(forward.z, 0, -forward.x);
targetDirection = h * right + 3 * forward;
moveDirection = targetDirection;
moveDirection = moveDirection.normalized;
oldForwardVector = forward;
}
// Strafing
if (Input.GetAxisRaw("Strafe") != 0)
{
strafe = Input.GetAxisRaw("Strafe");
Debug.Log("srafe: "+strafe);
strafe *= 5;
}
}
// In air controls
else
{
// Lock camera while in air
if (jumping)
lockCameraTimer = 0.0;
if (isMoving)
inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;
}
}
function ApplyWallJump ()
{
// We must actually jump against a wall for this to work
if (!jumping)
return;
// Store when we first touched a wall during this jump
if (collisionFlags == CollisionFlags.CollidedSides)
{
touchWallJumpTime = Time.time;
}
// The user can trigger a wall jump by hitting the button shortly before or shortly after hitting the wall the first time.
var mayJump = lastJumpButtonTime > touchWallJumpTime - wallJumpTimeout lastJumpButtonTime < touchWallJumpTime + wallJumpTimeout;
if (!mayJump)
return;
// Prevent jumping too fast after each other
if (lastJumpTime + jumpRepeatTime > Time.time)
return;
if (Mathf.Abs(wallJumpContactNormal.y) < 0.2)
{
wallJumpContactNormal.y = 0;
moveDirection = wallJumpContactNormal.normalized;
// Wall jump gives us at least trotspeed
moveSpeed = Mathf.Clamp(moveSpeed * 1.5, trotSpeed, runSpeed);
}
else
{
moveSpeed = 0;
}
verticalSpeed = CalculateJumpVerticalSpeed (jumpHeight);
DidJump();
SendMessage("DidWallJump", null, SendMessageOptions.DontRequireReceiver);
}
function ApplyJumping ()
{
// Prevent jumping too fast after each other
if (lastJumpTime + jumpRepeatTime > Time.time)
return;
if (IsGrounded()) {
// Jump
// - Only when pressing the button down
// - With a timeout so you can press the button slightly before landing
if (canJump Time.time < lastJumpButtonTime + jumpTimeout) {
verticalSpeed = CalculateJumpVerticalSpeed (jumpHeight);
SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
}
}
}
function ApplyGravity ()
{
if (isControllable) // don't move player at all if not controllable.
{
// Apply gravity
var jumpButton = Input.GetButton("Jump");
// * When falling down we use controlledDescentGravity (only when holding down jump)
var controlledDescent = canControlDescent verticalSpeed <= 0.0 jumpButton jumping;
// When we reach the apex of the jump we send out a message
if (jumping !jumpingReachedApex verticalSpeed <= 0.0)
{
jumpingReachedApex = true;
SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);
}
// * When jumping up we don't apply gravity for some time when the user is holding the jump button
// This gives more control over jump height by pressing the button longer
var extraPowerJump = IsJumping () verticalSpeed > 0.0 jumpButton transform.position.y < lastJumpStartHeight + extraJumpHeight;
if (controlledDescent)
verticalSpeed -= controlledDescentGravity * Time.deltaTime;
else if (extraPowerJump)
return;
else if (IsGrounded ())
verticalSpeed = 0.0;
else
verticalSpeed -= gravity * Time.deltaTime;
}
}
function CalculateJumpVerticalSpeed (targetJumpHeight : float)
{
// From the jump height and gravity we deduce the upwards speed
// for the character to reach at the apex.
return Mathf.Sqrt(2 * targetJumpHeight * gravity);
}
function DidJump ()
{
jumping = true;
jumpingReachedApex = false;
lastJumpTime = Time.time;
lastJumpStartHeight = transform.position.y;
touchWallJumpTime = -1;
lastJumpButtonTime = -10;
}
function Update() {
if (!isControllable)
{
// kill all inputs if not controllable.
Input.ResetInputAxes();
}
if (Input.GetButtonDown ("Jump"))
{
lastJumpButtonTime = Time.time;
}
UpdateSmoothedMovementDirection();
// Apply gravity
// - extra power jump modifies gravity
// - controlledDescent mode modifies gravity
ApplyGravity ();
// Perform a wall jump logic
// - Make sure we are jumping against wall etc.
// - Then apply jump in the right direction)
if (canWallJump)
ApplyWallJump();
// Apply jumping logic
ApplyJumping ();
// Calculate actual motion
// If moving forwards
forward = transform.TransformDirection(Vector3.forward);
forward = forward.normalized;
right = Vector3(forward.z, 0, -forward.x);
right = right.normalized;
if (strafe != 0 moveSpeed == 0)
{
moveSpeed = .2;
}
if (movingBack == false)
{
var movement = moveDirection * moveSpeed + Vector3 (0, verticalSpeed, strafe) + inAirVelocity;
movement *= Time.deltaTime;
}
else
{
movement = (moveDirection * -1) * moveSpeed + Vector3 (0, verticalSpeed, strafe) + inAirVelocity;
movement *= Time.deltaTime;
}
if (!isMoving)
{
//Debug.Log("No movement");
//movement = right * strafe * 5 + Vector3 (0, verticalSpeed, 0) + inAirVelocity;
//movement *= Time.deltaTime;
}
else
{
//Debug.Log("Movement");
}
//Debug.Log("movement: "+movement+ " moveSpeed: "+moveSpeed);
// Move the controller
var controller : CharacterController = GetComponent(CharacterController);
wallJumpContactNormal = Vector3.zero;
collisionFlags = controller.Move(movement);
// Set rotation to the move direction
if (IsGrounded())
{
if(slammed) // we got knocked over by an enemy. We need to reset some stuff
{
slammed = false;
controller.height = 2;
transform.position.y += 0.75;
}
transform.rotation = Quaternion.LookRotation(moveDirection);
}
else
{
if(!slammed)
{
var xzMove = movement;
xzMove.y = 0;
if (xzMove.sqrMagnitude > 0.001)
{
//transform.rotation = Quaternion.LookRotation(xzMove);
}
}
}
// We are in jump mode but just became grounded
if (IsGrounded())
{
lastGroundedTime = Time.time;
inAirVelocity = Vector3.zero;
if (jumping)
{
jumping = false;
SendMessage("DidLand", SendMessageOptions.DontRequireReceiver);
}
}
}
function OnControllerColliderHit (hit : ControllerColliderHit )
{
if (hit.moveDirection.y > 0.01)
return;
wallJumpContactNormal = hit.normal;
}
function GetSpeed () {
return moveSpeed;
}
function IsJumping () {
return jumping !slammed;
}
function IsGrounded () {
return (collisionFlags CollisionFlags.CollidedBelow) != 0;
}
function SuperJump (height : float)
{
verticalSpeed = CalculateJumpVerticalSpeed (height);
collisionFlags = CollisionFlags.None;
SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
}
function SuperJump (height : float, jumpVelocity : Vector3)
{
verticalSpeed = CalculateJumpVerticalSpeed (height);
inAirVelocity = jumpVelocity;
collisionFlags = CollisionFlags.None;
SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
}
function Slam (direction : Vector3)
{
verticalSpeed = CalculateJumpVerticalSpeed (1);
inAirVelocity = direction * 6;
direction.y = 0.6;
Quaternion.LookRotation(-direction);
var controller : CharacterController = GetComponent(CharacterController);
controller.height = 0.5;
slammed = true;
collisionFlags = CollisionFlags.None;
SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
}
function GetDirection () {
return moveDirection;
}
function IsMovingBackwards () {
return movingBack;
}
function GetLockCameraTimer ()
{
return lockCameraTimer;
}
function IsMoving () : boolean
{
return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5;
}
function HasJumpReachedApex ()
{
return jumpingReachedApex;
}
function IsGroundedWithTimeout ()
{
return lastGroundedTime + groundedTimeout > Time.time;
}
function IsControlledDescent ()
{
// * When falling down we use controlledDescentGravity (only when holding down jump)
var jumpButton = Input.GetButton("Jump");
return canControlDescent verticalSpeed <= 0.0 jumpButton jumping;
}
function Reset ()
{
gameObject.tag = "Player";
}
// Require a character controller to be attached to the same game object
@script RequireComponent(CharacterController)
@script AddComponentMenu("Third Person Player/Third Person Controller")
I need help with the collision for breaking the ray and the strafing.