【Unity3D】 官方自带人物控制器研究
发布日期:2021-06-30 11:40:35 浏览次数:3 分类:技术文章

本文共 56559 字,大约阅读时间需要 188 分钟。

文章目录

1.下载地址

5.0.0f4版本的官方自带资源包http://pan.baidu.com/s/1o8Ujrxo

2017年Unity的官方自带资源包http://pan.baidu.com/s/1ge3cUdX
有的就不用下载了,如果没有的话下载下载 ,放在xx\Editor路径下面,重新打开Unity3d就有了

2.导入

这里写图片描述

这里写图片描述
Assets->Standard Assets ->Characters ->FirstPersonCharacter ->Prefabs
选择预制体拖入到场景中就可以使用了

3. 应用

有两个预制体

FPSController.cs

主要组件有Character Controller、脚本First Person Controller、Rigidbody

这个是FPS第一人称控制器,模拟FPS游戏中人物移动的方式,是第一人称控制器。
鼠标锁定,视角跟随鼠标移动而移动。WSAD控制人物移动
这里写图片描述

RigidBodyFPSController.cs

主要组件有Capsule Collider、脚本RigidBody First Person Controller

与FPSController控制器不同的一点是,一个是用CharacterController控制移动,一个是控制人物本身的刚体,给刚体添加一个方向力,就可以移动
这里写图片描述

4.详细解析脚本

First Person Controller.cs

using System;using UnityEngine;using UnityStandardAssets.CrossPlatformInput;using UnityStandardAssets.Utility;using Random = UnityEngine.Random;namespace UnityStandardAssets.Characters.FirstPerson{
//自动添加关联的脚本 [RequireComponent(typeof (CharacterController))] [RequireComponent(typeof (AudioSource))] public class FirstPersonController : MonoBehaviour {
//判断是否在走 [SerializeField] private bool m_IsWalking; //走路的速度 [SerializeField] private float m_WalkSpeed; //奔跑的速度 [SerializeField] private float m_RunSpeed; //模仿随机行走的速度 [SerializeField] [Range(0f, 1f)] private float m_RunstepLenghten; //跳跃速度 [SerializeField] private float m_JumpSpeed; //判断是否在空中,如果在空中接给一个下降的力 [SerializeField] private float m_StickToGroundForce; //重力 [SerializeField] private float m_GravityMultiplier; //视角控制脚本 [SerializeField] private MouseLook m_MouseLook; [SerializeField] private bool m_UseFovKick; //FovKick脚本 [SerializeField] private FOVKick m_FovKick = new FOVKick(); [SerializeField] private bool m_UseHeadBob; [SerializeField] private CurveControlledBob m_HeadBob = new CurveControlledBob(); [SerializeField] private LerpControlledBob m_JumpBob = new LerpControlledBob(); [SerializeField] private float m_StepInterval; [SerializeField] private AudioClip[] m_FootstepSounds; // an array of footstep sounds that will be randomly selected from. [SerializeField] private AudioClip m_JumpSound; // the sound played when character leaves the ground. [SerializeField] private AudioClip m_LandSound; // the sound played when character touches back on ground. private Camera m_Camera; private bool m_Jump; private float m_YRotation; private Vector2 m_Input; private Vector3 m_MoveDir = Vector3.zero; private CharacterController m_CharacterController; private CollisionFlags m_CollisionFlags; private bool m_PreviouslyGrounded; private Vector3 m_OriginalCameraPosition; private float m_StepCycle; private float m_NextStep; private bool m_Jumping; private AudioSource m_AudioSource; // Use this for initialization private void Start() {
m_CharacterController = GetComponent
(); m_Camera = Camera.main; m_OriginalCameraPosition = m_Camera.transform.localPosition; m_FovKick.Setup(m_Camera); m_HeadBob.Setup(m_Camera, m_StepInterval); m_StepCycle = 0f; m_NextStep = m_StepCycle/2f; m_Jumping = false; m_AudioSource = GetComponent
(); m_MouseLook.Init(transform , m_Camera.transform); } // Update is called once per frame private void Update() {
//视角控制 RotateView(); // the jump state needs to read here to make sure it is not missed //跳转状态判断 if (!m_Jump) {
m_Jump = CrossPlatformInputManager.GetButtonDown("Jump"); } //判断是否在地面上 if (!m_PreviouslyGrounded && m_CharacterController.isGrounded) {
StartCoroutine(m_JumpBob.DoBobCycle()); PlayLandingSound(); m_MoveDir.y = 0f; m_Jumping = false; } //不在地面上,并且不在跳跃状态 if (!m_CharacterController.isGrounded && !m_Jumping && m_PreviouslyGrounded) {
m_MoveDir.y = 0f; } m_PreviouslyGrounded = m_CharacterController.isGrounded; } //播放降落的声音 private void PlayLandingSound() {
m_AudioSource.clip = m_LandSound; m_AudioSource.Play(); m_NextStep = m_StepCycle + .5f; } //控制人物行走 private void FixedUpdate() {
float speed; GetInput(out speed); // always move along the camera forward as it is the direction that it being aimed at //始终沿着摄像机向前移动,因为它是瞄准的方向 Vector3 desiredMove = transform.forward*m_Input.y + transform.right*m_Input.x; // get a normal for the surface that is being touched to move along it //得到一个正常的表面,被触摸移动它 RaycastHit hitInfo; Physics.SphereCast(transform.position, m_CharacterController.radius, Vector3.down, out hitInfo, m_CharacterController.height/2f); desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized; m_MoveDir.x = desiredMove.x*speed; m_MoveDir.z = desiredMove.z*speed; if (m_CharacterController.isGrounded) {
m_MoveDir.y = -m_StickToGroundForce; if (m_Jump) {
m_MoveDir.y = m_JumpSpeed; PlayJumpSound(); m_Jump = false; m_Jumping = true; } } else {
m_MoveDir += Physics.gravity*m_GravityMultiplier*Time.fixedDeltaTime; } m_CollisionFlags = m_CharacterController.Move(m_MoveDir*Time.fixedDeltaTime); ProgressStepCycle(speed); UpdateCameraPosition(speed); } //播放跳跃的声音 private void PlayJumpSound() {
m_AudioSource.clip = m_JumpSound; m_AudioSource.Play(); } private void ProgressStepCycle(float speed) {
if (m_CharacterController.velocity.sqrMagnitude > 0 && (m_Input.x != 0 || m_Input.y != 0)) {
m_StepCycle += (m_CharacterController.velocity.magnitude + (speed*(m_IsWalking ? 1f : m_RunstepLenghten)))* Time.fixedDeltaTime; } if (!(m_StepCycle > m_NextStep)) {
return; } m_NextStep = m_StepCycle + m_StepInterval; PlayFootStepAudio(); } //播放脚本的声音 private void PlayFootStepAudio() {
if (!m_CharacterController.isGrounded) {
return; } // pick & play a random footstep sound from the array, // excluding sound at index 0 int n = Random.Range(1, m_FootstepSounds.Length); m_AudioSource.clip = m_FootstepSounds[n]; m_AudioSource.PlayOneShot(m_AudioSource.clip); // move picked sound to index 0 so it's not picked next time m_FootstepSounds[n] = m_FootstepSounds[0]; m_FootstepSounds[0] = m_AudioSource.clip; } //控制摄像机的视角移动 private void UpdateCameraPosition(float speed) {
Vector3 newCameraPosition; if (!m_UseHeadBob) {
return; } if (m_CharacterController.velocity.magnitude > 0 && m_CharacterController.isGrounded) {
m_Camera.transform.localPosition = m_HeadBob.DoHeadBob(m_CharacterController.velocity.magnitude + (speed*(m_IsWalking ? 1f : m_RunstepLenghten))); newCameraPosition = m_Camera.transform.localPosition; newCameraPosition.y = m_Camera.transform.localPosition.y - m_JumpBob.Offset(); } else {
newCameraPosition = m_Camera.transform.localPosition; newCameraPosition.y = m_OriginalCameraPosition.y - m_JumpBob.Offset(); } m_Camera.transform.localPosition = newCameraPosition; } //获得键盘输入 private void GetInput(out float speed) {
// Read input float horizontal = CrossPlatformInputManager.GetAxis("Horizontal"); float vertical = CrossPlatformInputManager.GetAxis("Vertical"); bool waswalking = m_IsWalking;#if !MOBILE_INPUT // On standalone builds, walk/run speed is modified by a key press. // keep track of whether or not the character is walking or running m_IsWalking = !Input.GetKey(KeyCode.LeftShift);#endif // set the desired speed to be walking or running speed = m_IsWalking ? m_WalkSpeed : m_RunSpeed; m_Input = new Vector2(horizontal, vertical); // normalize input if it exceeds 1 in combined length: if (m_Input.sqrMagnitude > 1) {
m_Input.Normalize(); } // handle speed change to give an fov kick // only if the player is going to a run, is running and the fovkick is to be used if (m_IsWalking != waswalking && m_UseFovKick && m_CharacterController.velocity.sqrMagnitude > 0) {
StopAllCoroutines(); StartCoroutine(!m_IsWalking ? m_FovKick.FOVKickUp() : m_FovKick.FOVKickDown()); } } //选择视角到正常角度 private void RotateView() {
m_MouseLook.LookRotation (transform, m_Camera.transform); } //控制器碰撞反应 private void OnControllerColliderHit(ControllerColliderHit hit) {
Rigidbody body = hit.collider.attachedRigidbody; //dont move the rigidbody if the character is on top of it if (m_CollisionFlags == CollisionFlags.Below) {
return; } if (body == null || body.isKinematic) {
return; } body.AddForceAtPosition(m_CharacterController.velocity*0.1f, hit.point, ForceMode.Impulse); } }}

核心人物移动代码

m_CollisionFlags = m_CharacterController.Move(m_MoveDir*Time.fixedDeltaTime);m_CollisionFlags 碰撞检测的旗标m_CharacterController 角色的CharacterController组件m_MoveDir 当前移动的方向乘上键盘获得的输入得到的值Time.fixedDeltaTime 固定的时间增量

其中如果要解除鼠标锁定的话可以到这个脚本中修改

MouseLook.cs

//更新鼠标锁定的状态的		public void UpdateCursorLock()        {
//if the user set "lockCursor" we check & properly lock the cursos if (lockCursor) InternalLockUpdate(); } //控制鼠标锁定 private void InternalLockUpdate() {
if (Input.GetKeyUp(KeyCode.Escape)) {
m_cursorIsLocked = false; } else if (Input.GetMouseButtonUp(1)) {
m_cursorIsLocked = true; } if (m_cursorIsLocked) {
Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } else if (!m_cursorIsLocked) {
Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } }

RigidbodyFirstPersonController.cs

using System;using UnityEngine;using UnityStandardAssets.CrossPlatformInput;namespace UnityStandardAssets.Characters.FirstPerson{
//自动添加关联的脚本 [RequireComponent(typeof (Rigidbody))] [RequireComponent(typeof (CapsuleCollider))] public class RigidbodyFirstPersonController : MonoBehaviour {
[Serializable] public class MovementSettings {
//前进速度 public float ForwardSpeed = 8.0f; // Speed when walking forward //后退速度 public float BackwardSpeed = 4.0f; // Speed when walking backwards //走路时速度横向 public float StrafeSpeed = 4.0f; // Speed when walking sideways //奔跑的速度 public float RunMultiplier = 2.0f; // Speed when sprinting //奔跑键设置为LeftShift public KeyCode RunKey = KeyCode.LeftShift; //跳跃的力 public float JumpForce = 30f; //动画曲线,用在了模型动画播放时的碰撞盒缩放及重力调节 public AnimationCurve SlopeCurveModifier = new AnimationCurve(new Keyframe(-90.0f, 1.0f), new Keyframe(0.0f, 1.0f), new Keyframe(90.0f, 0.0f)); //当前的目标速度 [HideInInspector] public float CurrentTargetSpeed = 8f;#if !MOBILE_INPUT private bool m_Running;#endif //更新所需的目标速度 public void UpdateDesiredTargetSpeed(Vector2 input) {
if (input == Vector2.zero) return; if (input.x > 0 || input.x < 0) {
//strafe CurrentTargetSpeed = StrafeSpeed; } if (input.y < 0) {
//backwards CurrentTargetSpeed = BackwardSpeed; } if (input.y > 0) {
//forwards //handled last as if strafing and moving forward at the same time forwards speed should take precedence CurrentTargetSpeed = ForwardSpeed; }#if !MOBILE_INPUT if (Input.GetKey(RunKey)) {
CurrentTargetSpeed *= RunMultiplier; m_Running = true; } else {
m_Running = false; }#endif }#if !MOBILE_INPUT public bool Running {
get {
return m_Running; } }#endif } //高级设置 [Serializable] public class AdvancedSettings {
//检查控制器是否接地的距离(0.01f似乎最适合这个) public float groundCheckDistance = 0.01f; // distance for checking if the controller is grounded ( 0.01f seems to work best for this ) //停止这个角色 public float stickToGroundHelperDistance = 0.5f; // stops the character //当没有输入时控制器到达停止的速度 public float slowDownRate = 20f; // rate at which the controller comes to a stop when there is no input //用户能够控制在空气中移动的方向吗 public bool airControl; // can the user control the direction that is being moved in the air } public Camera cam; public MovementSettings movementSettings = new MovementSettings(); public MouseLook mouseLook = new MouseLook(); public AdvancedSettings advancedSettings = new AdvancedSettings(); private Rigidbody m_RigidBody; private CapsuleCollider m_Capsule; private float m_YRotation; private Vector3 m_GroundContactNormal; private bool m_Jump, m_PreviouslyGrounded, m_Jumping, m_IsGrounded; public Vector3 Velocity {
get {
return m_RigidBody.velocity; } } public bool Grounded {
get {
return m_IsGrounded; } } public bool Jumping {
get {
return m_Jumping; } } public bool Running {
get {
#if !MOBILE_INPUT return movementSettings.Running;#else return false;#endif } } private void Start() {
m_RigidBody = GetComponent
(); m_Capsule = GetComponent
(); mouseLook.Init (transform, cam.transform); } private void Update() {
//控制视角转动到正常位置 RotateView(); //跳跃状态判断转移 if (CrossPlatformInputManager.GetButtonDown("Jump") && !m_Jump) {
m_Jump = true; } } //移动函数 private void FixedUpdate() {
//判断底部与地面的距离 GroundCheck(); //获得输入 Vector2 input = GetInput(); //如果检测到键盘有输入的话 if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) && (advancedSettings.airControl || m_IsGrounded)) {
// always move along the camera forward as it is the direction that it being aimed at //始终沿着摄像机向前移动,因为它是瞄准的方向 Vector3 desiredMove = cam.transform.forward*input.y + cam.transform.right*input.x; desiredMove = Vector3.ProjectOnPlane(desiredMove, m_GroundContactNormal).normalized; desiredMove.x = desiredMove.x*movementSettings.CurrentTargetSpeed; desiredMove.z = desiredMove.z*movementSettings.CurrentTargetSpeed; desiredMove.y = desiredMove.y*movementSettings.CurrentTargetSpeed; //判断刚体上面的速度向量的平方是否 小于 当前移动速度的平方 if (m_RigidBody.velocity.sqrMagnitude < (movementSettings.CurrentTargetSpeed*movementSettings.CurrentTargetSpeed)) {
//给刚体添加一个向前的作用力 m_RigidBody.AddForce(desiredMove*SlopeMultiplier(), ForceMode.Impulse); } } //是否在地面上,判断跳跃 if (m_IsGrounded) {
m_RigidBody.drag = 5f; if (m_Jump) {
m_RigidBody.drag = 0f; m_RigidBody.velocity = new Vector3(m_RigidBody.velocity.x, 0f, m_RigidBody.velocity.z); m_RigidBody.AddForce(new Vector3(0f, movementSettings.JumpForce, 0f), ForceMode.Impulse); m_Jumping = true; } if (!m_Jumping && Mathf.Abs(input.x) < float.Epsilon && Mathf.Abs(input.y) < float.Epsilon && m_RigidBody.velocity.magnitude < 1f) {
m_RigidBody.Sleep(); } } else {
m_RigidBody.drag = 0f; if (m_PreviouslyGrounded && !m_Jumping) {
StickToGroundHelper(); } } m_Jump = false; } //斜率乘数 private float SlopeMultiplier() {
float angle = Vector3.Angle(m_GroundContactNormal, Vector3.up); return movementSettings.SlopeCurveModifier.Evaluate(angle); } //判断是否在地面上,不在地面上就落在地面上 private void StickToGroundHelper() {
RaycastHit hitInfo; if (Physics.SphereCast(transform.position, m_Capsule.radius, Vector3.down, out hitInfo, ((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.stickToGroundHelperDistance)) {
if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f) {
m_RigidBody.velocity = Vector3.ProjectOnPlane(m_RigidBody.velocity, hitInfo.normal); } } } //检测键盘输入 private Vector2 GetInput() {
Vector2 input = new Vector2 {
x = CrossPlatformInputManager.GetAxis("Horizontal"), y = CrossPlatformInputManager.GetAxis("Vertical") }; movementSettings.UpdateDesiredTargetSpeed(input); return input; } //视角移动 private void RotateView() {
//avoids the mouse looking if the game is effectively paused if (Mathf.Abs(Time.timeScale) < float.Epsilon) return; // get the rotation before it's changed float oldYRotation = transform.eulerAngles.y; mouseLook.LookRotation (transform, cam.transform); if (m_IsGrounded || advancedSettings.airControl) {
// Rotate the rigidbody velocity to match the new direction that the character is looking Quaternion velRotation = Quaternion.AngleAxis(transform.eulerAngles.y - oldYRotation, Vector3.up); m_RigidBody.velocity = velRotation*m_RigidBody.velocity; } } /// sphere cast down just beyond the bottom of the capsule to see if the capsule is colliding round the bottom //球体在胶囊的底部被压下,看看胶囊是否在底部碰撞 private void GroundCheck() {
m_PreviouslyGrounded = m_IsGrounded; RaycastHit hitInfo; if (Physics.SphereCast(transform.position, m_Capsule.radius, Vector3.down, out hitInfo, ((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.groundCheckDistance)) {
m_IsGrounded = true; m_GroundContactNormal = hitInfo.normal; } else {
m_IsGrounded = false; m_GroundContactNormal = Vector3.up; } if (!m_PreviouslyGrounded && m_IsGrounded && m_Jumping) {
m_Jumping = false; } } }}

核心人物移动代码

m_RigidBody.AddForce(desiredMove*SlopeMultiplier(), ForceMode.Impulse);desiredMove: 这个是获取到摄像机正前方 x 键盘输入 Vertical的值,摄像机右边 x 键盘输入 Horizontal的值SlopeMuliplier: 是斜率乘数,说白了就是摩擦力 反作用力ForceMode 作用力方式 枚举类型(1)ForceMode.Force:默认方式,使用刚体的质量计算,以每帧间隔时间为单位计算动量。(2)ForceMode.Acceleration:在此种作用方式下会忽略刚体的实际质量而采用默认值1.0f,时间间隔以系统帧频间隔计算(默认值为0.02s)(3)ForceMode.Impulse:此种方式采用瞬间力作用方式,即把t的值默认为1,不再采用系统的帧频间隔(4)ForceMode.VelocityChange:此种作用方式下将忽略刚体的实际质量,采用默认质量1.0,同时也忽略系统的实际帧频间隔,采用默认间隔1.0

5.接下来是官方第一人称控制器的自带的摇杆

这里写图片描述

接下来介绍这几种预制体

CarTiltControls.cs

这里写图片描述

这里写图片描述
这里写图片描述
用于赛车游戏的跨平台输入控制。可选择两种输入形式:

一,Vertical轴输入值由一对按钮控制,Horizontal轴输入值由设备重力感应控制(目标平台为PC时使用鼠标位置模拟,下同);

二,两个方向的输入值均由触屏滑动(移动设备)或鼠标拖拽(PC)控制。

DualTouchControls.cs

这里写图片描述

这里写图片描述
这里写图片描述

TouchPad.cs

using System;using UnityEngine;using UnityEngine.EventSystems;using UnityEngine.UI;namespace UnityStandardAssets.CrossPlatformInput{
//自动添加的组件 [RequireComponent(typeof(Image))] public class TouchPad : MonoBehaviour, IPointerDownHandler, IPointerUpHandler {
// Options for which axes to use //使用那个轴 public enum AxisOption {
Both, // Use both OnlyHorizontal, // Only horizontal OnlyVertical // Only vertical } //控制方式 public enum ControlStyle {
//绝对的,从图片的中心操作 Absolute, // operates from teh center of the image //相对的,从触碰到的中心开始操作 Relative, // operates from the center of the initial touch //从滑动触碰开始,没有中心 Swipe, // swipe to touch touch no maintained center } public AxisOption axesToUse = AxisOption.Both; // The options for the axes that the still will use public ControlStyle controlStyle = ControlStyle.Absolute; // control style to use public string horizontalAxisName = "Horizontal"; // The name given to the horizontal axis for the cross platform input public string verticalAxisName = "Vertical"; // The name given to the vertical axis for the cross platform input public float Xsensitivity = 1f; public float Ysensitivity = 1f; //起始点 Vector3 m_StartPos; //上一个点 Vector2 m_PreviousDelta; //Joytick输出 Vector3 m_JoytickOutput; //使用X或者Y轴 bool m_UseX; // Toggle for using the x axis bool m_UseY; // Toggle for using the Y axis //参考交叉平台输入中的操纵杆 CrossPlatformInputManager.VirtualAxis m_HorizontalVirtualAxis; // Reference to the joystick in the cross platform input CrossPlatformInputManager.VirtualAxis m_VerticalVirtualAxis; // Reference to the joystick in the cross platform input //是否在拖动 bool m_Dragging; int m_Id = -1; //刷卡方式控制触摸 Vector2 m_PreviousTouchPos; // swipe style control touch#if !UNITY_EDITOR private Vector3 m_Center; private Image m_Image;#else //鼠标开始的位置 Vector3 m_PreviousMouse;#endif void OnEnable() {
//创建虚拟轴 CreateVirtualAxes();#if !UNITY_EDITOR m_Image = GetComponent(); m_Center = m_Image.transform.position;#endif } //创建虚拟轴 void CreateVirtualAxes() {
// set axes to use //设置轴使用 m_UseX = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyHorizontal); m_UseY = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyVertical); // create new axes based on axes to use //创建新的基于使用的坐标轴 if (m_UseX) {
m_HorizontalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(horizontalAxisName); CrossPlatformInputManager.RegisterVirtualAxis(m_HorizontalVirtualAxis); } if (m_UseY) {
m_VerticalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(verticalAxisName); CrossPlatformInputManager.RegisterVirtualAxis(m_VerticalVirtualAxis); } } //更新轴的位置 void UpdateVirtualAxes(Vector3 value) {
value = value.normalized; if (m_UseX) {
m_HorizontalVirtualAxis.Update(value.x); } if (m_UseY) {
m_VerticalVirtualAxis.Update(value.y); } } //按下指示器 public void OnPointerDown(PointerEventData data) {
m_Dragging = true; m_Id = data.pointerId;#if !UNITY_EDITOR if (controlStyle != ControlStyle.Absolute ) m_Center = data.position;#endif } //获得拖动的坐标并且更新坐标轴 void Update() {
if (!m_Dragging) {
return; } if (Input.touchCount == 2) {
if (Input.GetTouch(0).phase == TouchPhase.Moved || Input.GetTouch(1).phase == TouchPhase.Moved) {
var tempPosition1 = Input.GetTouch(0).position; var tempPosition2 = Input.GetTouch(1).position; float sprtTemp = Mathf.Sqrt((tempPosition1.x - tempPosition2.x) * (tempPosition1.x - tempPosition2.x) + (tempPosition1.y - tempPosition2.y) * (tempPosition1.y - tempPosition2.y)); } } //手指点击的时候id是1,但是如果点住之后不松开然后滑动的话下面的if就会执行 if (Input.touchCount >= m_Id + 1 && m_Id != -1) {
#if !UNITY_EDITOR if (controlStyle == ControlStyle.Swipe) {
m_Center = m_PreviousTouchPos; m_PreviousTouchPos = Input.touches[m_Id].position; } Vector2 pointerDelta = new Vector2(Input.touches[m_Id].position.x - m_Center.x , Input.touches[m_Id].position.y - m_Center.y).normalized; pointerDelta.x *= Xsensitivity; pointerDelta.y *= Ysensitivity;#else Vector2 pointerDelta; pointerDelta.x = Input.mousePosition.x - m_PreviousMouse.x; pointerDelta.y = Input.mousePosition.y - m_PreviousMouse.y; m_PreviousMouse = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0f);#endif UpdateVirtualAxes(new Vector3(pointerDelta.x, pointerDelta.y, 0)); } } //松开指示器 public void OnPointerUp(PointerEventData data) {
m_Dragging = false; m_Id = -1; UpdateVirtualAxes(Vector3.zero); } //程序关闭调用 void OnDisable() {
if (CrossPlatformInputManager.AxisExists(horizontalAxisName)) CrossPlatformInputManager.UnRegisterVirtualAxis(horizontalAxisName); if (CrossPlatformInputManager.AxisExists(verticalAxisName)) CrossPlatformInputManager.UnRegisterVirtualAxis(verticalAxisName); } }}

这里写图片描述

左边手指滑动可以控制行走
右边手指滑动可以控制视角转动
Jump就是跳
演示了多点触控情景下TouchPad脚本的使用方式,通过将不同区域的滑动或拖拽数据映射到不同的虚拟轴来避免冲突。

MobileAircraftControls.cs

这里写图片描述

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

用于飞行器的跨平台输入控制。主要演示了ButtonHandler脚本的使用,自动将触控或鼠标指针的按下和抬起映射为特定虚拟轴的状态变化。同时将重力感应数据映射为横向和纵向输入值。

MoblieSingleStickControl.cs

这里写图片描述

这里写图片描述
这里写图片描述
主要演示Joystick脚本的使用,通过滑动或拖拽控制输入,与TouchPad的区别在于
MobileJoystick的使用是根据手指拖动的距离来移动
而TouchPad是与上一帧位置间的距离作为输入值

MobileTiltControlRig.cs

无UI元素,可在代码中通过CrossPlatformInputManager获取其映射轴的值,输入来源为重力感应数据(移动平台)或鼠标位置(PC)。

这里写图片描述
这里写图片描述

7.摇杆使用的注意事项

首先如果不是在安卓平台的话,直接把预制体拖入层级视图Hierarchy是不会显示的

首先切换成安卓平台 File->BUild Settings->选中安卓平台Android->Switch platform就行了
然后把预制体拖入到层级视图里面就显示了

最后附上一段Joystick脚本在不同方向显示不同sprite的代码

也就是人们常说的八方向控制摇杆移动的方法
判断角度控制摇杆移动

Joystick脚本,这个脚本我已经修改过了,加了一个8个方向显示不同的Sprite的效果

using System;using UnityEngine;using UnityEngine.EventSystems;using UnityEngine.UI;namespace UnityStandardAssets.CrossPlatformInput{
public class Joystick : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler {
public Sprite[] sourceImage; private Image mobileJoystickImage; private Image arrowImage; private Image backgroundImage; public Material[] materialsImage; //虚拟轴的选择 public enum AxisOption {
// Options for which axes to use //两种都有 Both, // Use both //只有横轴 OnlyHorizontal, // Only horizontal //只有纵轴 OnlyVertical // Only vertical } //移动的幅度 public int MovementRange = 100; //使用的是个虚拟轴 public AxisOption axesToUse = AxisOption.Both; // The options for the axes that the still will use public string horizontalAxisName = "Horizontal"; // The name given to the horizontal axis for the cross platform input public string verticalAxisName = "Vertical"; // The name given to the vertical axis for the cross platform input //初始点 Vector3 m_StartPos; //使用x轴 bool m_UseX; // Toggle for using the x axis //使用y轴 bool m_UseY; // Toggle for using the Y axis //处理跨平台的虚拟轴input数据,然后移动 CrossPlatformInputManager.VirtualAxis m_HorizontalVirtualAxis; // Reference to the joystick in the cross platform input CrossPlatformInputManager.VirtualAxis m_VerticalVirtualAxis; // Reference to the joystick in the cross platform input void Start() {
mobileJoystickImage = GameObject.Find("MobileJoystick").GetComponent(); arrowImage = GameObject.Find("ArrowImage").GetComponent(); backgroundImage = GameObject.Find("BackgroundImage").GetComponent(); arrowImage.enabled = false; backgroundImage.enabled = false; } void OnEnable() {
//初始点的位置 m_StartPos = transform.localPosition; //创建虚拟轴的位置 CreateVirtualAxes(); } //更新虚拟轴的位置 void UpdateVirtualAxes(Vector3 value) {
//value是手指拖动虚拟轴移动后的位置 var delta = value - m_StartPos; delta /= MovementRange; if (m_UseX) {
//更新x轴的坐标 m_HorizontalVirtualAxis.Update(delta.x); } if (m_UseY) {
//更新y轴的坐标 m_VerticalVirtualAxis.Update(delta.y); } } //创建虚拟轴的位置 void CreateVirtualAxes() {
// set axes to use //设置轴使用 m_UseX = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyHorizontal); m_UseY = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyVertical); // create new axes based on axes to use //创建基于坐标轴的新轴使用 if (m_UseX) {
m_HorizontalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(horizontalAxisName); CrossPlatformInputManager.RegisterVirtualAxis(m_HorizontalVirtualAxis); } if (m_UseY) {
m_VerticalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(verticalAxisName); CrossPlatformInputManager.RegisterVirtualAxis(m_VerticalVirtualAxis); } } //手指拖动虚拟杆的时候调用这个函数 public void OnDrag(PointerEventData data) {
arrowImage.enabled = true; backgroundImage.enabled = true; Vector3 newPos = Vector3.zero; //根据拖动的坐标值返回角度 float angles = Mathf.Atan2(data.position.y - m_StartPos.x, data.position.x - m_StartPos.y) * Mathf.Rad2Deg; #region 四个方向 //if (angles > -45 && angles < 0 || angles > 45 && angles < 90) //{
// //right // mobileJoystickImage.sprite = sourceImage[3]; //} //else if (angles > 90 && angles < 135) //{
// //forware // mobileJoystickImage.sprite = sourceImage[0]; //} //else if (angles > 135 && angles < 179 || angles > -145 && angles < -180) //{
// //left // mobileJoystickImage.sprite = sourceImage[2]; //} //else if (angles < -45 && angles > -135) //{
// //back // mobileJoystickImage.sprite = sourceImage[1]; //} //if (data.position.x == 200 && data.position.y == 200) //{
// //origin // mobileJoystickImage.sprite = sourceImage[4]; //} #endregion #region 八个方向 if (angles > 67.5 && angles < 112.5) {
//up arrowImage.sprite = sourceImage[0]; } else if (angles > 112.5 && angles < 157.5) {
//leftup arrowImage.sprite = sourceImage[4]; } else if (angles > 157.5 && angles < 180 || angles > -180 && angles < -157.5) {
//left arrowImage.sprite = sourceImage[2]; } else if (angles > -157.5 && angles < -112.5) {
//leftdown arrowImage.sprite = sourceImage[5]; } else if (angles > -112.5 && angles < -67.5) {
//down arrowImage.sprite = sourceImage[1]; } else if (angles > -67.5 && angles < -22.5) {
//rightdown arrowImage.sprite = sourceImage[7]; } else if (angles > -22.5 && angles < 22.5) {
//right arrowImage.sprite = sourceImage[3]; } else if (angles > 22.5 && angles < 67.5) {
//rightup arrowImage.sprite = sourceImage[6]; } #endregion //返回x轴的值 if (m_UseX) {
int delta = (int)(data.position.x - m_StartPos.x); delta = Mathf.Clamp(delta, -MovementRange, MovementRange); newPos.x = delta; } //返回y轴的值 if (m_UseY) {
int delta = (int)(data.position.y - m_StartPos.y); delta = Mathf.Clamp(delta, -MovementRange, MovementRange); newPos.y = delta; } //虚拟轴移动到的位置 transform.position = new Vector3(m_StartPos.x + newPos.x, m_StartPos.y + newPos.y, m_StartPos.z + newPos.z); //更新虚拟轴的位置 UpdateVirtualAxes(transform.position); } //松开虚拟轴 public void OnPointerUp(PointerEventData data) {
mobileJoystickImage.sprite = sourceImage[8]; arrowImage.enabled = false; backgroundImage.enabled = false; transform.position = m_StartPos; UpdateVirtualAxes(m_StartPos); } //按下虚拟轴 public void OnPointerDown(PointerEventData data) {
} //从交叉平台的输入中删除操纵杆 void OnDisable() {
// remove the joysticks from the cross platform input if (m_UseX) {
m_HorizontalVirtualAxis.Remove(); } if (m_UseY) {
m_VerticalVirtualAxis.Remove(); } } }}

还有就是如果想要joystick的按钮有动态的显示,比如按下之后高亮,或者开始隐藏,点击之后出现,或者拖动按钮出位置之后隐藏这些功能的话可以添加 UGUI的Button组件

在这里插入图片描述

  • Normal Color 没有点击的时候的颜色
  • Highlighted Color高亮显示
  • Pressed Color按压的时候的颜色
  • Disabled Color 不在点击状态的时候的颜色
  • Color Multiplier 颜色乘数
  • Fade Duration 淡入淡出持续时间

设置这些颜色的alpha的值就可以了

8.第三人称控制器

这里写图片描述

有两个预制体,两个预制体不同的地方在于一个是AI有NavMeshAgent控制器
一个没有

AIThirdPersonController

重要组件 Animator、Rigidbody 、CapsuleCollider、NavMeshAgent、AICharacterControl、ThirdPersonCharactterr

这里写图片描述

AICharacterControl.cs

using System;using UnityEngine;namespace UnityStandardAssets.Characters.ThirdPerson{
[RequireComponent(typeof (NavMeshAgent))] [RequireComponent(typeof (ThirdPersonCharacter))] public class AICharacterControl : MonoBehaviour {
//获得自动寻路的NavMeshAgent public NavMeshAgent agent {
get; private set; } // the navmesh agent required for the path finding //脚本ThirdPersonCHaracter控制行走 public ThirdPersonCharacter character {
get; private set; } // the character we are controlling //目标点 public Transform target; // target to aim for // Use this for initialization private void Start() {
// get the components on the object we need ( should not be null due to require component so no need to check ) agent = GetComponentInChildren
(); character = GetComponent
(); agent.updateRotation = false; agent.updatePosition = true; } // Update is called once per frame private void Update() {
if (target != null) {
agent.SetDestination(target.position); // use the values to move the character //使用值来移动字符 character.Move(agent.desiredVelocity, false, false); } else {
// We still need to call the character's move function, but we send zeroed input as the move param. // 仍然调用Move函数,但是输入zero点 character.Move(Vector3.zero, false, false); } } //获得目标点 public void SetTarget(Transform target) {
this.target = target; } }}

ThirdPersonCharacter.cs

using UnityEngine;namespace UnityStandardAssets.Characters.ThirdPerson{
//自动关联组件 [RequireComponent(typeof(Rigidbody))] [RequireComponent(typeof(CapsuleCollider))] [RequireComponent(typeof(Animator))] public class ThirdPersonCharacter : MonoBehaviour {
//人物转向的速度 [SerializeField] float m_MovingTurnSpeed = 360; //固定的转动速度 [SerializeField] float m_StationaryTurnSpeed = 180; //跳跃的高度 [SerializeField] float m_JumpPower = 12f; //重力乘数 [Range(1f, 4f)][SerializeField] float m_GravityMultiplier = 2f; //跑的时候的偏移值 [SerializeField] float m_RunCycleLegOffset = 0.2f; //specific to the character in sample assets, will need to be modified to work with others //移动速度乘数 [SerializeField] float m_MoveSpeedMultiplier = 1f; //动画播放速度乘数 [SerializeField] float m_AnimSpeedMultiplier = 1f; //地面检查距离 [SerializeField] float m_GroundCheckDistance = 0.1f; Rigidbody m_Rigidbody; Animator m_Animator; //是否接地 bool m_IsGrounded; //接地坚持距离 float m_OrigGroundCheckDistance; //一半 const float k_Half = 0.5f; //转动的数量 float m_TurnAmount; //正向的数量 float m_ForwardAmount; //接地的坐标轴 Vector3 m_GroundNormal; //胶囊体的高度 float m_CapsuleHeight; //胶囊体的中心点 Vector3 m_CapsuleCenter; //胶囊碰撞体 CapsuleCollider m_Capsule; //蹲伏 bool m_Crouching; void Start() {
m_Animator = GetComponent
(); m_Rigidbody = GetComponent
(); m_Capsule = GetComponent
(); //获得组件上面的胶囊碰撞体的高度和中心点 m_CapsuleHeight = m_Capsule.height; m_CapsuleCenter = m_Capsule.center; //m_Rigidbody.constraints控制自由度的自由度可以用来模拟这个刚性体 m_Rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY | RigidbodyConstraints.FreezeRotationZ; m_OrigGroundCheckDistance = m_GroundCheckDistance; } //人物移动函数 public void Move(Vector3 move, bool crouch, bool jump) {
// convert the world relative moveInput vector into a local-relative // turn amount and forward amount required to head in the desired // direction. //如果获得的向量的长度大于1,就让它标准化向量 if (move.magnitude > 1f) move.Normalize(); //逆向 move = transform.InverseTransformDirection(move); //地面检测 CheckGroundStatus(); //Vector3.ProjectOnPlane将向量投影到平面上的正交正交平面上的向量 move = Vector3.ProjectOnPlane(move, m_GroundNormal); m_TurnAmount = Mathf.Atan2(move.x, move.z); m_ForwardAmount = move.z; //转向的速度 ApplyExtraTurnRotation(); // control and velocity handling is different when grounded and airborne: //地面和空中的控制和速度处理是不同的: if (m_IsGrounded) //在地面上 {
HandleGroundedMovement(crouch, jump); } else //不在地面上 {
HandleAirborneMovement(); } //如果胶囊体是蹲伏状态 ScaleCapsuleForCrouching(crouch); //防止站在低处 PreventStandingInLowHeadroom(); // send input and other state parameters to the animator //向动画器发送输入和其他状态参数 UpdateAnimator(move); } //胶囊体是蹲伏状态 void ScaleCapsuleForCrouching(bool crouch) {
if (m_IsGrounded && crouch) {
if (m_Crouching) return; m_Capsule.height = m_Capsule.height / 2f; m_Capsule.center = m_Capsule.center / 2f; m_Crouching = true; } else {
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up); float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half; if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength)) {
m_Crouching = true; return; } m_Capsule.height = m_CapsuleHeight; m_Capsule.center = m_CapsuleCenter; m_Crouching = false; } } //防止站在低处 void PreventStandingInLowHeadroom() {
// prevent standing up in crouch-only zones if (!m_Crouching) {
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up); float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half; if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength)) {
m_Crouching = true; } } } //向动画器发送输入和其他状态参数 void UpdateAnimator(Vector3 move) {
// update the animator parameters m_Animator.SetFloat("Forward", m_ForwardAmount, 0.1f, Time.deltaTime); m_Animator.SetFloat("Turn", m_TurnAmount, 0.1f, Time.deltaTime); m_Animator.SetBool("Crouch", m_Crouching); m_Animator.SetBool("OnGround", m_IsGrounded); if (!m_IsGrounded) {
m_Animator.SetFloat("Jump", m_Rigidbody.velocity.y); } // calculate which leg is behind, so as to leave that leg trailing in the jump animation // (This code is reliant on the specific run cycle offset in our animations, // and assumes one leg passes the other at the normalized clip times of 0.0 and 0.5) float runCycle = Mathf.Repeat( m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime + m_RunCycleLegOffset, 1); float jumpLeg = (runCycle < k_Half ? 1 : -1) * m_ForwardAmount; if (m_IsGrounded) {
m_Animator.SetFloat("JumpLeg", jumpLeg); } // the anim speed multiplier allows the overall speed of walking/running to be tweaked in the inspector, // which affects the movement speed because of the root motion. if (m_IsGrounded && move.magnitude > 0) {
m_Animator.speed = m_AnimSpeedMultiplier; } else {
// don't use that while airborne m_Animator.speed = 1; } } //处理机载运动 void HandleAirborneMovement() {
// apply extra gravity from multiplier: Vector3 extraGravityForce = (Physics.gravity * m_GravityMultiplier) - Physics.gravity; m_Rigidbody.AddForce(extraGravityForce); m_GroundCheckDistance = m_Rigidbody.velocity.y < 0 ? m_OrigGroundCheckDistance : 0.01f; } //处理接地运动 void HandleGroundedMovement(bool crouch, bool jump) {
// check whether conditions are right to allow a jump: if (jump && !crouch && m_Animator.GetCurrentAnimatorStateInfo(0).IsName("Grounded")) {
// jump! m_Rigidbody.velocity = new Vector3(m_Rigidbody.velocity.x, m_JumpPower, m_Rigidbody.velocity.z); m_IsGrounded = false; m_Animator.applyRootMotion = false; m_GroundCheckDistance = 0.1f; } } //转向的速度 void ApplyExtraTurnRotation() {
// help the character turn faster (this is in addition to root rotation in the animation) //帮助字符转得更快(这是在动画的根旋转之外) float turnSpeed = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, m_ForwardAmount); transform.Rotate(0, m_TurnAmount * turnSpeed * Time.deltaTime, 0); } //动画移动 public void OnAnimatorMove() {
// we implement this function to override the default root motion. // this allows us to modify the positional speed before it's applied. if (m_IsGrounded && Time.deltaTime > 0) {
Vector3 v = (m_Animator.deltaPosition * m_MoveSpeedMultiplier) / Time.deltaTime; // we preserve the existing y part of the current velocity. v.y = m_Rigidbody.velocity.y; m_Rigidbody.velocity = v; } } //地面检测 void CheckGroundStatus() {
RaycastHit hitInfo;#if UNITY_EDITOR // helper to visualise the ground check ray in the scene view //在场景视图中显示地面检查光线的助手 Debug.DrawLine(transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * m_GroundCheckDistance));#endif// 0.1f is a small offset to start the ray from inside the character// it is also good to note that the transform position in the sample assets is at the base of the character// 0.1 f是一个小的偏移量,可以从字符内部开始射线// 值得注意的是,示例资产中的转换位置位于字符的底部 if (Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out hitInfo, m_GroundCheckDistance)) {
m_GroundNormal = hitInfo.normal; m_IsGrounded = true; m_Animator.applyRootMotion = true; } else {
m_IsGrounded = false; m_GroundNormal = Vector3.up; m_Animator.applyRootMotion = false; } } }}

ThirdPersonController.cs

重要组件 Animator、Rigidbody 、CapsuleCollider、ThirdPersonUserControl、ThirdPersonCharactterr

这里写图片描述

ThiredPersonUserControl.cs

using System;using UnityEngine;using UnityStandardAssets.CrossPlatformInput;namespace UnityStandardAssets.Characters.ThirdPerson{
[RequireComponent(typeof (ThirdPersonCharacter))] public class ThirdPersonUserControl : MonoBehaviour {
//引用对象上的ThirdPersonCharacter private ThirdPersonCharacter m_Character; // A reference to the ThirdPersonCharacter on the object private Transform m_Cam; // A reference to the main camera in the scenes transform private Vector3 m_CamForward; // The current forward direction of the camera private Vector3 m_Move; private bool m_Jump; // the world-relative desired move direction, calculated from the camForward and user input. private void Start() {
// get the transform of the main camera if (Camera.main != null) {
m_Cam = Camera.main.transform; } else {
Debug.LogWarning( "Warning: no main camera found. Third person character needs a Camera tagged \"MainCamera\", for camera-relative controls."); // we use self-relative controls in this case, which probably isn't what the user wants, but hey, we warned them! } // get the third person character ( this should never be null due to require component ) m_Character = GetComponent
(); } private void Update() {
if (!m_Jump) {
m_Jump = CrossPlatformInputManager.GetButtonDown("Jump"); } } //移动函数 // Fixed update is called in sync with physics private void FixedUpdate() {
// read inputs float h = CrossPlatformInputManager.GetAxis("Horizontal"); float v = CrossPlatformInputManager.GetAxis("Vertical"); bool crouch = Input.GetKey(KeyCode.C); // calculate move direction to pass to character if (m_Cam != null) {
// calculate camera relative direction to move: m_CamForward = Vector3.Scale(m_Cam.forward, new Vector3(1, 0, 1)).normalized; m_Move = v*m_CamForward + h*m_Cam.right; } else {
// we use world-relative directions in the case of no main camera m_Move = v*Vector3.forward + h*Vector3.right; }#if !MOBILE_INPUT // walk speed multiplier if (Input.GetKey(KeyCode.LeftShift)) m_Move *= 0.5f;#endif // pass all parameters to the character control script m_Character.Move(m_Move, crouch, m_Jump); m_Jump = false; } }}

ThirdPersonCharacter.cs

using UnityEngine;namespace UnityStandardAssets.Characters.ThirdPerson{
//自动关联组件 [RequireComponent(typeof(Rigidbody))] [RequireComponent(typeof(CapsuleCollider))] [RequireComponent(typeof(Animator))] public class ThirdPersonCharacter : MonoBehaviour {
//人物转向的速度 [SerializeField] float m_MovingTurnSpeed = 360; //固定的转动速度 [SerializeField] float m_StationaryTurnSpeed = 180; //跳跃的高度 [SerializeField] float m_JumpPower = 12f; //重力乘数 [Range(1f, 4f)][SerializeField] float m_GravityMultiplier = 2f; //跑的时候的偏移值 [SerializeField] float m_RunCycleLegOffset = 0.2f; //specific to the character in sample assets, will need to be modified to work with others //移动速度乘数 [SerializeField] float m_MoveSpeedMultiplier = 1f; //动画播放速度乘数 [SerializeField] float m_AnimSpeedMultiplier = 1f; //地面检查距离 [SerializeField] float m_GroundCheckDistance = 0.1f; Rigidbody m_Rigidbody; Animator m_Animator; //是否接地 bool m_IsGrounded; //接地坚持距离 float m_OrigGroundCheckDistance; //一半 const float k_Half = 0.5f; //转动的数量 float m_TurnAmount; //正向的数量 float m_ForwardAmount; //接地的坐标轴 Vector3 m_GroundNormal; //胶囊体的高度 float m_CapsuleHeight; //胶囊体的中心点 Vector3 m_CapsuleCenter; //胶囊碰撞体 CapsuleCollider m_Capsule; //蹲伏 bool m_Crouching; void Start() {
m_Animator = GetComponent
(); m_Rigidbody = GetComponent
(); m_Capsule = GetComponent
(); //获得组件上面的胶囊碰撞体的高度和中心点 m_CapsuleHeight = m_Capsule.height; m_CapsuleCenter = m_Capsule.center; //m_Rigidbody.constraints控制自由度的自由度可以用来模拟这个刚性体 m_Rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY | RigidbodyConstraints.FreezeRotationZ; m_OrigGroundCheckDistance = m_GroundCheckDistance; } //人物移动函数 public void Move(Vector3 move, bool crouch, bool jump) {
// convert the world relative moveInput vector into a local-relative // turn amount and forward amount required to head in the desired // direction. //如果获得的向量的长度大于1,就让它标准化向量 if (move.magnitude > 1f) move.Normalize(); //逆向 move = transform.InverseTransformDirection(move); //地面检测 CheckGroundStatus(); //Vector3.ProjectOnPlane将向量投影到平面上的正交正交平面上的向量 move = Vector3.ProjectOnPlane(move, m_GroundNormal); m_TurnAmount = Mathf.Atan2(move.x, move.z); m_ForwardAmount = move.z; //转向的速度 ApplyExtraTurnRotation(); // control and velocity handling is different when grounded and airborne: //地面和空中的控制和速度处理是不同的: if (m_IsGrounded) //在地面上 {
HandleGroundedMovement(crouch, jump); } else //不在地面上 {
HandleAirborneMovement(); } //如果胶囊体是蹲伏状态 ScaleCapsuleForCrouching(crouch); //防止站在低处 PreventStandingInLowHeadroom(); // send input and other state parameters to the animator //向动画器发送输入和其他状态参数 UpdateAnimator(move); } //胶囊体是蹲伏状态 void ScaleCapsuleForCrouching(bool crouch) {
if (m_IsGrounded && crouch) {
if (m_Crouching) return; m_Capsule.height = m_Capsule.height / 2f; m_Capsule.center = m_Capsule.center / 2f; m_Crouching = true; } else {
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up); float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half; if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength)) {
m_Crouching = true; return; } m_Capsule.height = m_CapsuleHeight; m_Capsule.center = m_CapsuleCenter; m_Crouching = false; } } //防止站在低处 void PreventStandingInLowHeadroom() {
// prevent standing up in crouch-only zones if (!m_Crouching) {
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up); float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half; if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength)) {
m_Crouching = true; } } } //向动画器发送输入和其他状态参数 void UpdateAnimator(Vector3 move) {
// update the animator parameters m_Animator.SetFloat("Forward", m_ForwardAmount, 0.1f, Time.deltaTime); m_Animator.SetFloat("Turn", m_TurnAmount, 0.1f, Time.deltaTime); m_Animator.SetBool("Crouch", m_Crouching); m_Animator.SetBool("OnGround", m_IsGrounded); if (!m_IsGrounded) {
m_Animator.SetFloat("Jump", m_Rigidbody.velocity.y); } // calculate which leg is behind, so as to leave that leg trailing in the jump animation // (This code is reliant on the specific run cycle offset in our animations, // and assumes one leg passes the other at the normalized clip times of 0.0 and 0.5) float runCycle = Mathf.Repeat( m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime + m_RunCycleLegOffset, 1); float jumpLeg = (runCycle < k_Half ? 1 : -1) * m_ForwardAmount; if (m_IsGrounded) {
m_Animator.SetFloat("JumpLeg", jumpLeg); } // the anim speed multiplier allows the overall speed of walking/running to be tweaked in the inspector, // which affects the movement speed because of the root motion. if (m_IsGrounded && move.magnitude > 0) {
m_Animator.speed = m_AnimSpeedMultiplier; } else {
// don't use that while airborne m_Animator.speed = 1; } } //处理机载运动 void HandleAirborneMovement() {
// apply extra gravity from multiplier: Vector3 extraGravityForce = (Physics.gravity * m_GravityMultiplier) - Physics.gravity; m_Rigidbody.AddForce(extraGravityForce); m_GroundCheckDistance = m_Rigidbody.velocity.y < 0 ? m_OrigGroundCheckDistance : 0.01f; } //处理接地运动 void HandleGroundedMovement(bool crouch, bool jump) {
// check whether conditions are right to allow a jump: if (jump && !crouch && m_Animator.GetCurrentAnimatorStateInfo(0).IsName("Grounded")) {
// jump! m_Rigidbody.velocity = new Vector3(m_Rigidbody.velocity.x, m_JumpPower, m_Rigidbody.velocity.z); m_IsGrounded = false; m_Animator.applyRootMotion = false; m_GroundCheckDistance = 0.1f; } } //转向的速度 void ApplyExtraTurnRotation() {
// help the character turn faster (this is in addition to root rotation in the animation) //帮助字符转得更快(这是在动画的根旋转之外) float turnSpeed = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, m_ForwardAmount); transform.Rotate(0, m_TurnAmount * turnSpeed * Time.deltaTime, 0); } //动画移动 public void OnAnimatorMove() {
// we implement this function to override the default root motion. // this allows us to modify the positional speed before it's applied. if (m_IsGrounded && Time.deltaTime > 0) {
Vector3 v = (m_Animator.deltaPosition * m_MoveSpeedMultiplier) / Time.deltaTime; // we preserve the existing y part of the current velocity. v.y = m_Rigidbody.velocity.y; m_Rigidbody.velocity = v; } } //地面检测 void CheckGroundStatus() {
RaycastHit hitInfo;#if UNITY_EDITOR // helper to visualise the ground check ray in the scene view //在场景视图中显示地面检查光线的助手 Debug.DrawLine(transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * m_GroundCheckDistance));#endif// 0.1f is a small offset to start the ray from inside the character// it is also good to note that the transform position in the sample assets is at the base of the character// 0.1 f是一个小的偏移量,可以从字符内部开始射线// 值得注意的是,示例资产中的转换位置位于字符的底部 if (Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out hitInfo, m_GroundCheckDistance)) {
m_GroundNormal = hitInfo.normal; m_IsGrounded = true; m_Animator.applyRootMotion = true; } else {
m_IsGrounded = false; m_GroundNormal = Vector3.up; m_Animator.applyRootMotion = false; } } }}

转载地址:https://itmonon.blog.csdn.net/article/details/78501613 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【Unity3D】 实现物体一闪一闪的效果,霓虹灯效果,跑马灯效果,LED灯
下一篇:【ITween】Unity3d iIween插件的研究

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月24日 23时09分03秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章