i achieved this with 3 scripts:
- Movement
- CamY
- CamScript
Movement.cs:
using UnityEngine;
using System.Collections;
public class Movement : MonoBehaviour
{
public float speed = 10;
public float jumpHeight = 5;
public float gravity = 9;
public Transform cam;
public CamScript camScript;
public CamY camY;
private Vector3 moveDir = Vector3.zero;
public float y = 0;
private CharacterController controller;
private Vector3 forward;
private float lastRot = 0;
private float camLastDist;
private bool wasInAir = false;
void Awake()
{
controller = GetComponent<CharacterController>();
}
void Update()
{
moveDir.x = Input.GetAxis("Horizontal");
moveDir.z = Input.GetAxis("Vertical");
if (moveDir.magnitude > 1)
{
moveDir.Normalize();
}
if (Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0)
{
forward = camY.SendTransformPosition(moveDir);
transform.eulerAngles = new Vector3(0, Mathf.MoveTowardsAngle(transform.eulerAngles.y, (Mathf.Atan2(forward.x, forward.z) * Mathf.Rad2Deg), Time.deltaTime * 500), 0);
}
else
{
forward = Vector3.zero;
}
if (controller.isGrounded)
{
if (wasInAir)
{
y = -gravity / 2;
wasInAir = false;
}
if (Input.GetKeyDown("space"))
{
y = jumpHeight;
}
}
else
{
wasInAir = true;
y -= gravity * Time.deltaTime;
}
controller.Move(new Vector3(forward.x * speed, y, forward.z * speed) * Time.deltaTime);
RaycastHit hitInfo;
//Debug.DrawLine(transform.position, cam.position + Vector3.down * 0.4f, Color.blue);
if (Physics.Linecast(transform.position, cam.position + Vector3.down * 0.4f, out hitInfo))
{
camScript.isColliding = true;
camScript.distance = Vector3.Distance(hitInfo.point, transform.position) + 0.4f;
camLastDist = camScript.distance;
}
else
{
camScript.isColliding = false;
//Debug.DrawLine(transform.position, camScript.GetCamsLastPos(camLastDist), Color.red);
if (!Physics.Linecast(transform.position, camScript.GetCamsLastPos(camLastDist)) !camScript.isChanging)
{
camScript.StartCoroutine(camScript.ReturnToNormalDistance());
}
}
}
}
CamScript.cs:
using UnityEngine;
using System.Collections;
public class CamScript : MonoBehaviour
{
public Transform target;
public float distance = 10.0f;
public float staticDist = 10.0f;
public bool isColliding = false;
public bool isChanging = false;
public float xSpeed = 250.0f;
public float ySpeed = 120.0f;
public float yMinLimit = -20f;
public float yMaxLimit = 80f;
public float plusY = 0;
public Transform camY;
public float x = 0.0f;
public float y = 0.0f;
float xVelocity = 0;
float yVelocity = 0;
float distVelocity = 0;
Quaternion rotation;
Vector3 position;
void Awake()
{
Transform camYInstance = Instantiate(camY) as Transform;
camYInstance.GetComponent<CamY>().cam = transform;
target.GetComponent<Movement>().camY = camYInstance.GetComponent<CamY>();
}
void Start()
{
Vector3 angles = transform.eulerAngles;
x = angles.y;
y = angles.x;
}
void LateUpdate()
{
if (Input.GetMouseButton(0))
{
x += Input.GetAxis("Mouse X") * xSpeed;
y -= Input.GetAxis("Mouse Y") * ySpeed;
}
staticDist = Mathf.SmoothStep(staticDist, staticDist - Input.GetAxis("Mouse ScrollWheel") * 60, 25 * Time.deltaTime);
staticDist = Mathf.Clamp(staticDist, 5, 30);
y = ClampAngle(y, yMinLimit, yMaxLimit);
rotation = Quaternion.Euler(y, x, 0);
position = rotation * new Vector3(0.0f, 0.0f, -distance) + (target.position + Vector3.up * plusY);
transform.rotation = rotation;
transform.position = position;
}
static float ClampAngle(float angle, float min, float max)
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp(angle, min, max);
}
public IEnumerator ReturnToNormalDistance()
{
isChanging = true;
while (distance != staticDist isColliding == false)
{
distance = Mathf.MoveTowards(distance, staticDist, Time.deltaTime * 10);
yield return 0;
}
isChanging = false;
}
public Vector3 GetCamsLastPos(float lastDist)
{
return rotation * new Vector3(0.0f, 0.0f, -lastDist) + target.position;
}
}
CamY.cs:
using UnityEngine;
using System.Collections;
public class CamY : MonoBehaviour
{
public Transform cam;
void Update()
{
transform.eulerAngles = new Vector3(0, cam.eulerAngles.y, 0);
}
public Vector3 SendTransformPosition(Vector3 dir)
{
return transform.TransformDirection(dir);
}
}
you put the movement.cs in the character, make sure it has CharacterController attached to it, also set his layer to Ignore Raycast.
make a prefab of empty gameobject and put the CamY script in it.
attached CamScript.cs into your main camera, attach CamY prefab into your CamY variable in the CamScript inspector.
attach the player’s object into the variable “target” in the CamScript inspector.
set all the rest of the values… if you have problems with your camera’s collision detection, you will need to rewrite it, because i used it for my game’s needs. good luck.