Played “World of Warcraft” friends all know, “World of Warcraft” role controller is very good, Unity3D standard package comes with a third person and first-person character controller, but the feeling of the top a lot worse, the following Third-person character controller to imitate the “World of Warcraft,” I hope you like it.
As usual, let’s take a look at the final effect, we can control the role of the keyboard and mouse rotation, movement, vision closer and so on, as shown:
The third person role controller main class UPlayerController.cs, the code is as follows:
indent preformatted text by 4 spaces
using UnityEngine;
using System.Collections;
public class UPlayerController : MonoBehaviour
{
///
/// 待机动画
///
public AnimationClip idleClip;
/// <summary>
/// 行走动画
/// </summary>
public AnimationClip walkClip;
/// <summary>
/// 跑步动画
/// </summary>
public AnimationClip runClip;
/// <summary>
/// 跳跃动画
/// </summary>
public AnimationClip jumpClip;
/// <summary>
/// 后退动画
/// </summary>
public AnimationClip retreatClip;
/// <summary>
/// 跑步移动速度
/// </summary>
public float runSpeed = 6.0f;
/// <summary>
/// 行走移动速度
/// </summary>
public float walkSpeed = 3.0f;
/// <summary>
/// 后退移动速度
/// </summary>
public float retreatSpeed = 2.0f;
/// <summary>
/// 移动速度
/// </summary>
public float speed = 0f;
/// <summary>
/// 摄像机对象
/// </summary>
public Transform cameraTransform;
/// <summary>
/// 角度头部对象
/// </summary>
public Transform headTransform;
/// <summary>
/// 摄像机偏离距离
/// </summary>
public float cameraDistance = 5f;
/// <summary>
/// 摄像机最近视角
/// </summary>
public float cameraZoomNear = 0.01f;
/// <summary>
/// 摄像机最远视角
/// </summary>
public float cameraZoomFar = 30.0f;
/// <summary>
/// 鼠标移动 X 坐标轴
/// </summary>
public float mouseXAxis = 15.0f;
/// <summary>
/// 鼠标移动 Y 坐标轴
/// </summary>
public float mouseYAxis = 15.0f;
/// <summary>
/// 鼠标滚轮速率
/// </summary>
public float mouseWheelVelocity = 3.0f;
/// <summary>
/// 跳起速率
/// </summary>
public float jumpVelocity = 15f;
/// <summary>
/// 下落速率
/// </summary>
public float dropVelocity = 10f;
/// <summary>
/// 摄像机位置修正
/// </summary>
public float cameraReviseDistance = 1.0f;
/// <summary>
/// 最小位置
/// </summary>
public float cameraClampMin = -70f;
/// <summary>
/// 最大位置
/// </summary>
public float cameraClampMax = 90f;
/// <summary>
/// 偏移角度
/// </summary>
public float offsetAngle = 90f;
/// <summary>
/// 移动向量,通过计算得来
/// </summary>
protected Vector3 moveDirection;
/// <summary>
/// 垂直速率
/// </summary>
protected float verticalVelocity;
/// <summary>
/// 屏幕鼠标 X 坐标
/// </summary>
protected float mouseX;
/// <summary>
/// 屏幕鼠标 Y 坐标
/// </summary>
protected float mouseY;
/// <summary>
/// 前一次鼠标 X 坐标
/// </summary>
protected float prevMouseX;
/// <summary>
/// 前一次鼠标 Y 坐标
/// </summary>
protected float prevMouseY;
/// <summary>
/// 前一次旋转
/// </summary>
protected Quaternion prevQuaternion;
/// <summary>
/// 前一次朝向
/// </summary>
protected Vector3 prevForward;
/// <summary>
/// 是否正在起跳
/// </summary>
protected bool isJumping;
/// <summary>
/// 插值转向变量
/// </summary>
protected float mouseXVelocity;
/// <summary>
/// 插值跳起变量
/// </summary>
protected float currentJumpVelocity;
/// <summary>
/// 角色当前状态
/// </summary>
protected string roleActionEnum;
/// <summary>
/// Q、E 移动向量
/// </summary>
protected float horizontalQEDirection;
/// <summary>
/// 水平方向向量
/// </summary>
protected float horizontalDirection;
/// <summary>
/// 垂直方向向量
/// </summary>
protected float verticalDirection;
/// <summary>
/// 鼠标 X 属性
/// </summary>
protected float mouseXDirection;
/// <summary>
/// 鼠标 Y 属性
/// </summary>
protected float mouseYDirection;
/// <summary>
/// 鼠标滚轮属性
/// </summary>
protected float mouseScrollWheelDirection;
/// <summary>
/// 水平移动数据
/// </summary>
protected float horizontalValue;
/// <summary>
/// 垂直移动数据
/// </summary>
protected float verticalValue;
/// <summary>
/// 跳起状态
/// </summary>
protected bool jumpStatus;
/// <summary>
/// 鼠标左键按下状态
/// </summary>
protected bool buttonDownLeftStatus;
/// <summary>
/// 鼠标左键弹起状态
/// </summary>
protected bool buttonUpLeftStatus;
/// <summary>
/// 鼠标左键状态
/// </summary>
protected bool buttonLeftStatus;
/// <summary>
/// 鼠标右键状态
/// </summary>
protected bool buttonRightStatus;
/// <summary>
/// 左边 Shift 状态
/// </summary>
protected bool shiftLeftStatus;
/// <summary>
/// 右边 Shift 状态
/// </summary>
protected bool shiftRightStatus;
/// <summary>
/// 角色控制器
/// </summary>
private CharacterController characterController;
/// <summary>
/// 动画
/// </summary>
private Animation animation;
void Awake ()
{
if (this.cameraTransform == null && Camera.main != null) this.cameraTransform = Camera.main.transform;
if (this.cameraTransform == null) return;
this.characterController = this.GetComponentInChildren<CharacterController> ();
this.animation = this.GetComponentInChildren<Animation> ();
this.cameraTransform.parent = this.headTransform;
this.cameraTransform.localPosition = Vector3.zero;
this.cameraTransform.position = this.transform.position;
this.verticalVelocity = 0f;
this.animation.CrossFade (this.idleClip.name);
}
/// <summary>
/// 初始化输入数据
/// </summary>
protected virtual void InitInputData()
{
if (this.cameraTransform == null) return;
this.horizontalQEDirection = 0f;
this.horizontalDirection = Input.GetAxisRaw ("Horizontal");
this.verticalDirection = Input.GetAxisRaw ("Vertical");
this.mouseXDirection = Input.GetAxis ("Mouse X") * this.mouseXAxis;
this.mouseYDirection = Input.GetAxis ("Mouse Y") * this.mouseYAxis;
this.mouseScrollWheelDirection = Input.GetAxis ("Mouse ScrollWheel");
this.buttonDownLeftStatus = Input.GetMouseButtonDown ((int)UMouseTypeEnum.LEFT);
this.buttonLeftStatus = Input.GetMouseButton ((int)UMouseTypeEnum.LEFT);
this.buttonRightStatus = Input.GetMouseButton ((int)UMouseTypeEnum.RIGHT);
this.buttonUpLeftStatus = Input.GetMouseButtonUp ((int)UMouseTypeEnum.LEFT);
if (!buttonLeftStatus && Input.GetKey (KeyCode.Q)) this.horizontalQEDirection = -1f;
if (!buttonLeftStatus && Input.GetKey (KeyCode.E)) this.horizontalQEDirection = 1f;
this.jumpStatus = Input.GetKey (KeyCode.Space);
this.shiftLeftStatus = Input.GetKey (KeyCode.LeftShift);
this.shiftRightStatus = Input.GetKey (KeyCode.RightShift);
}
void Update()
{
if (this.cameraTransform == null) return;
this.InitInputData ();
// 获取地面状态
bool groundStatus = this.IsGrounded ();
if (groundStatus && this.jumpStatus && !this.isJumping)
{
this.isJumping = true;
this.verticalVelocity = this.jumpVelocity;
}
if (this.isJumping && this.verticalVelocity >= 0f)
{
this.verticalVelocity = Mathf.SmoothDamp(this.verticalVelocity, -this.dropVelocity, ref this.currentJumpVelocity, 0.15f);
}
else
{
this.isJumping = false;
this.verticalVelocity = -this.dropVelocity;
}
// 如果按住滚轮
if (this.mouseScrollWheelDirection != 0)
{
if (this.cameraDistance >= this.cameraZoomNear && this.cameraDistance <= this.cameraZoomFar)
{
this.cameraDistance -= this.mouseScrollWheelDirection * this.mouseWheelVelocity;
}
if (this.cameraDistance < this.cameraZoomNear)
{
this.cameraDistance = this.cameraZoomNear;
}
if (this.cameraDistance > this.cameraZoomFar)
{
this.cameraDistance = this.cameraZoomFar;
}
this.cameraDistance = Mathf.Max(0.01f, this.cameraDistance);
}
// 如果鼠标左键按下状态
if (this.buttonDownLeftStatus)
{
this.prevMouseX = mouseX;
this.prevMouseY = mouseY;
this.prevForward = this.cameraTransform.TransformDirection(Vector3.forward);
}
// 如果按下鼠标左键或者按下鼠标右键
if (this.buttonLeftStatus || this.buttonRightStatus)
{
this.mouseX += this.mouseXDirection;
this.mouseY += this.mouseYDirection;
}
else
{
this.mouseX += 0f;
this.mouseY += 0f;
}
// 如果鼠标左键松开
if (this.buttonUpLeftStatus)
{
this.mouseX = this.prevMouseX;
this.mouseY = this.prevMouseY;
}
// 如果按下了水平方向键,并且鼠标未按下,左右转换视角
if (this.horizontalDirection != 0f && (!this.buttonLeftStatus && !this.buttonRightStatus))
{
if (this.horizontalDirection > 0)
{
this.mouseX = Mathf.SmoothDamp (this.mouseX, this.mouseX + 90f, ref this.mouseXVelocity, 0.2f);
}
else
{
this.mouseX = Mathf.SmoothDamp (this.mouseX, this.mouseX - 90f, ref this.mouseXVelocity, 0.2f);
}
}
// 如果按下了水平方向键,并且鼠标右键,左右移动
else if(this.horizontalDirection != 0f && this.buttonRightStatus)
{
this.horizontalQEDirection = this.horizontalDirection;
}
this.mouseY = Mathf.Clamp (this.mouseY, this.cameraClampMin, this.cameraClampMax);
Quaternion xQuaternion = Quaternion.AngleAxis (this.mouseX, Vector3.up);
Quaternion yQuaternion = Quaternion.AngleAxis (this.mouseY, Vector3.left);
// 当前旋转角度
this.prevQuaternion = xQuaternion * yQuaternion;
// 如果Q、E水平移动
if (this.horizontalQEDirection != 0)
{
if (this.verticalDirection != 0f)
{
// 如果水平轴大于零,比如按下了 D 键
if(this.horizontalQEDirection > 0)
{
xQuaternion = Quaternion.AngleAxis(this.mouseX + this.offsetAngle * 0.5f, Vector3.up);
}
else // 如果水平轴小于零,比如按下了 A 键
{
xQuaternion = Quaternion.AngleAxis(this.mouseX - this.offsetAngle * 0.5f, Vector3.up);
}
}
else
{
// 如果水平轴大于零,比如按下了 D 键
if(this.horizontalQEDirection > 0)
{
xQuaternion = Quaternion.AngleAxis(this.mouseX + this.offsetAngle, Vector3.up);
}
else // 如果水平轴小于零,比如按下了 A 键
{
xQuaternion = Quaternion.AngleAxis(this.mouseX - this.offsetAngle, Vector3.up);
}
}
}
if (!this.buttonLeftStatus)
{
this.transform.rotation = xQuaternion;
}
// 摄像机对象的偏离位置,相对于角色头部位置
this.cameraTransform.localPosition = new Vector3 (0f, 0f, this.headTransform.transform.localPosition.z - this.cameraDistance);
// 如果按住了 Shift 键
if (this.shiftLeftStatus || this.shiftRightStatus)
{
this.speed = this.walkSpeed;
this.roleActionEnum = URoleActionEnum.WALK;
} else {
this.speed = this.runSpeed;
this.roleActionEnum = URoleActionEnum.RUN;
}
if (verticalDirection < 0f)
{
this.speed = this.retreatSpeed;
this.roleActionEnum = URoleActionEnum.RETREAT;
}
this.horizontalValue = this.horizontalQEDirection * this.speed * Time.deltaTime;
this.verticalValue = this.verticalDirection * this.speed * Time.deltaTime;
// 如果在跳起状态
if (this.isJumping) this.roleActionEnum = URoleActionEnum.JUMP;
if (this.horizontalQEDirection == 0f && this.horizontalDirection == 0f && this.verticalValue == 0f && !this.isJumping) this.roleActionEnum = URoleActionEnum.IDLE;
Vector3 forward = this.cameraTransform.TransformDirection (Vector3.forward);
if (this.buttonLeftStatus) forward = this.prevForward;
forward.y = 0;
forward = forward.normalized;
Vector3 right = new Vector3 (forward.z, 0, -forward.x);
// 设置移动向量
this.moveDirection = this.horizontalValue * right + this.verticalValue * forward + new Vector3 (0, this.verticalVelocity * Time.deltaTime, 0);
// 设置对象状态动画
this.ChangeAction (this.roleActionEnum);
this.characterController.Move (this.moveDirection);
}
private void ChangeAction(string roleActionEnum)
{
if(roleActionEnum == URoleActionEnum.IDLE)
{
this.animation.CrossFade(this.idleClip.name);
}
else if(roleActionEnum == URoleActionEnum.WALK)
{
this.animation.CrossFade(this.walkClip.name);
}
else if(roleActionEnum == URoleActionEnum.RUN)
{
this.animation.CrossFade(this.runClip.name);
}
else if(roleActionEnum == URoleActionEnum.JUMP)
{
this.animation.CrossFade(this.jumpClip.name);
}
else if(roleActionEnum == URoleActionEnum.RETREAT)
{
this.animation.CrossFade(this.retreatClip.name);
}
}
void LateUpdate ()
{
if (this.cameraTransform == null) return;
// 设置头部旋转
this.headTransform.rotation = this.prevQuaternion;
// 下面代码保证视角内没有透视区域
RaycastHit hitCamera = new RaycastHit ();
RaycastHit[] hits = Physics.SphereCastAll (this.headTransform.position, 0.3f * 0.5f, -this.cameraTransform.forward, this.cameraDistance);
hitCamera.distance = Mathf.Infinity;
foreach (RaycastHit hit in hits)
{
//判断是否是地形
Terrain terrain = hit.transform.GetComponent<Terrain>();
if (hit.distance < hitCamera.distance && hit.transform != this.transform.transform && terrain != null)
{
bool found = false;
if (this.transform.transform.childCount > 0)
{
foreach (Transform childTransform in this.transform)
{
if (hit.transform == childTransform)
{
found = true;
break;
}
}
}
if (!found) hitCamera = hit;
}
}
if (hitCamera.distance != Mathf.Infinity)
{
this.cameraDistance -= this.cameraReviseDistance;
}
}
protected virtual bool IsGrounded ()
{
return (this.characterController.collisionFlags == CollisionFlags.CollidedBelow);
}
}
UMouseTypeEnum.cs
using UnityEngine;
using System.Collections;
public enum UMouseTypeEnum
{
LEFT = 0,
RIGHT = 1
}
URoleActionEnum.cs :
using UnityEngine;
using System.Collections;
public class URoleActionEnum
{
public const string IDLE = “idle”;
public const string WALK = "walk";
public const string RUN = "run";
public const string RETREAT = "retreat";
public const string JUMP = "jump";
public const string ATTACK = "attack";
public const string MAGIC = "magic";
public const string DIE = "die";
}