Unity3D模仿《魔兽世界》的第三人称角色控制器

转载自:http://www.omuying.com/article/111.aspx

玩过《魔兽世界》的朋友都知道,《魔兽世界》中的角色控制器非常的出色,Unity3D 的标准包中自带了第三人称与第一人称角色控制器,但是感觉上面差了很多,下面这个第三人称角色控制器模仿的《魔兽世界》,希望大家喜欢。

与往常一样,我们先来看看最终效果,我们可以通过键盘以及鼠标控制角色旋转、移动,视野拉近拉远等等,如图:

新建一个测试场景,要保证场景中有一个角色对象,如图:

在角色对象中添加一个空对象,取名:HeadItem,主要用来挂载主摄像机,如图:

第三人称角色控制器主要的类有 UPlayerController.cs,代码如下:

using UnityEngine;
using System.Collections;

public class UPlayerController : MonoBehaviour
{
	/// <summary>
	/// 待机动画
	/// </summary>
	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);
	}
}

然后给角色对象挂载 CharacterController 组件 和 UPlayerController.cs,如图:

UMouseTypeEnum

using UnityEngine;
using System.Collections;

public enum UMouseTypeEnum
{
	LEFT = 0,
	RIGHT = 1
}

URoleActionEnum

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";
}

运行游戏,我们就可以灵活的控制角色在场景中移动、旋转了!


  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值