操作类似“和平精英”操作,左半屏摇杆控制角色移动,右半屏滑动控制相机旋转。
已经把功能做成案例并打包成Package,下面是下载链接。https://download.csdn.net/download/ubuyaolan/76715554https://download.csdn.net/download/ubuyaolan/76715554
以下是关键代码
UI控制脚本 ,摇杆
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.EventSystems;using UnityEngine.UI;/*create by keefor on 2021/11/16*/public class Joystick : MonoBehaviour,IPointerDownHandler,IPointerUpHandler,IDragHandler{ public float m_Radius = 5.0f; public Image stickImg; public Image dragImg; /// <summary> /// 遥杆原点 /// </summary> Vector2 stickOrigin; #region 委托事件 public delegate void PointerDwon(Vector2 pos); public delegate void PointerUp(Vector2 pos); public delegate void DragSomthing(Vector2 pos); public event PointerDwon OnTouchDown; public event PointerUp OnTouchUp; public event DragSomthing OnDraging; #endregion public bool stay; //public Canvas canvas; private void Start() { if (stickImg == null) { if(transform.childCount>0) stickImg = transform.GetChild(0)?.GetComponent<Image>(); } if (dragImg == null) { if(stickImg!=null&&stickImg.transform.childCount>0) dragImg = stickImg.transform.GetChild(0)?.GetComponent<Image>(); } if(stickImg)m_Radius = stickImg.rectTransform.rect.size.x / 2; //Debug.Log(m_Radius); } public void OnDrag(PointerEventData eventData) { //Debug.Log("=============OnDrag==============:" + eventData.position); Vector2 pointer = eventData.position; Vector2 localPos = pointer - stickOrigin; if(dragImg)dragImg.rectTransform.anchoredPosition = Vector2.ClampMagnitude(localPos, this.m_Radius); //执行事件 if(OnDraging != null) { OnDraging(stay?localPos:localPos.normalized); } if (stay) stickOrigin = pointer; } public void OnPointerDown(PointerEventData eventData) { //Debug.Log("=============OnPointerDown==============:" + eventData.position); stickOrigin = eventData.position; if(OnTouchDown != null) { OnTouchDown(stickOrigin); } } public void OnPointerUp(PointerEventData eventData) { //Debug.Log("=============OnPointerUp==============:" + eventData.position); if(dragImg)dragImg.rectTransform.anchoredPosition = Vector2.zero; if(OnTouchUp != null) { OnTouchUp(eventData.position); } }}
模型控制脚本
using UnityEngine;#if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKEDusing UnityEngine.InputSystem;#endif/* Note: animations are called via the controller for both the character and capsule using animator null checks */namespace StarterAssets{[RequireComponent(typeof(CharacterController))]#if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKED[RequireComponent(typeof(PlayerInput))]#endifpublic class ThirdPersonController : MonoBehaviour{[Header("Player")][Tooltip("Move speed of the character in m/s")]public float MoveSpeed = 2.0f;[Tooltip("Sprint speed of the character in m/s")]public float SprintSpeed = 5.335f;[Tooltip("How fast the character turns to face movement direction")][Range(0.0f, 0.3f)]public float RotationSmoothTime = 0.12f;[Tooltip("Acceleration and deceleration")]public float SpeedChangeRate = 10.0f;[Space(10)][Tooltip("The height the player can jump")]public float JumpHeight = 1.2f;[Tooltip("The character uses its own gravity value. The engine default is -9.81f")]public float Gravity = -15.0f;[Space(10)][Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]public float JumpTimeout = 0.50f;[Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]public float FallTimeout = 0.15f;[Header("Player Grounded")][Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]public bool Grounded = true;[Tooltip("Useful for rough ground")]public float GroundedOffset = -0.14f;[Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]public float GroundedRadius = 0.28f;[Tooltip("What layers the character uses as ground")]public LayerMask GroundLayers;// playerprivate float _speed;private float _animationBlend;private float _targetRotation = 0.0f;private float _rotationVelocity;private float _verticalVelocity;private float _terminalVelocity = 53.0f;// timeout deltatimeprivate float _jumpTimeoutDelta;private float _fallTimeoutDelta;// animation IDsprivate int _animIDSpeed;private int _animIDGrounded;private int _animIDJump;private int _animIDFreeFall;private int _animIDMotionSpeed;private Animator _animator;private CharacterController _controller;private StarterAssetsInputs _input;private GameObject _mainCamera;// private const float _threshold = 0.01f;private bool _hasAnimator;private void Awake(){// get a reference to our main cameraif (_mainCamera == null){_mainCamera = GameObject.FindGameObjectWithTag("MainCamera");}}private void Start(){// _hasAnimator = TryGetComponent(out _animator);_animator = GetComponentInChildren<Animator>();_hasAnimator = _animator;_controller = GetComponent<CharacterController>();_input = GetComponent<StarterAssetsInputs>();AssignAnimationIDs();// reset our timeouts on start_jumpTimeoutDelta = JumpTimeout;_fallTimeoutDelta = FallTimeout;}private void Update(){//_hasAnimator = TryGetComponent(out _animator);JumpAndGravity();GroundedCheck();Move();}private void AssignAnimationIDs(){_animIDSpeed = Animator.StringToHash("Speed");_animIDGrounded = Animator.StringToHash("Grounded");_animIDJump = Animator.StringToHash("Jump");_animIDFreeFall = Animator.StringToHash("FreeFall");_animIDMotionSpeed = Animator.StringToHash("MotionSpeed");}private void GroundedCheck(){// set sphere position, with offsetVector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z);Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers, QueryTriggerInteraction.Ignore);// update animator if using characterif (_hasAnimator){_animator.SetBool(_animIDGrounded, Grounded);}}private void Move(){// set target speed based on move speed, sprint speed and if sprint is pressedfloat targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;// a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon// note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude// if there is no input, set the target speed to 0if (_input.move == Vector2.zero) targetSpeed = 0.0f;// a reference to the players current horizontal velocityfloat currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;float speedOffset = 0.1f;float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;// accelerate or decelerate to target speedif (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset){// creates curved result rather than a linear one giving a more organic speed change// note T in Lerp is clamped, so we don't need to clamp our speed_speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate);// round speed to 3 decimal places_speed = Mathf.Round(_speed * 1000f) / 1000f;}else{_speed = targetSpeed;}_animationBlend = Mathf.Lerp(_animationBlend, targetSpeed, Time.deltaTime * SpeedChangeRate);// normalise input directionVector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;// note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude// if there is a move input rotate player when the player is movingif (_input.move != Vector2.zero){_targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + _mainCamera.transform.eulerAngles.y;float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity, RotationSmoothTime);// rotate to face input direction relative to camera positiontransform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);}Vector3 targetDirection = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;// move the player_controller.Move(targetDirection.normalized * (_speed * Time.deltaTime) + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime);// update animator if using characterif (_hasAnimator){_animator.SetFloat(_animIDSpeed, _animationBlend);_animator.SetFloat(_animIDMotionSpeed, inputMagnitude);}}private void JumpAndGravity(){if (Grounded){// reset the fall timeout timer_fallTimeoutDelta = FallTimeout;// update animator if using characterif (_hasAnimator){_animator.SetBool(_animIDJump, false);_animator.SetBool(_animIDFreeFall, false);}// stop our velocity dropping infinitely when groundedif (_verticalVelocity < 0.0f){_verticalVelocity = -2f;}// Jumpif (_input.jump && _jumpTimeoutDelta <= 0.0f){// the square root of H * -2 * G = how much velocity needed to reach desired height_verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);// update animator if using characterif (_hasAnimator){_animator.SetBool(_animIDJump, true);}}// jump timeoutif (_jumpTimeoutDelta >= 0.0f){_jumpTimeoutDelta -= Time.deltaTime;}}else{// reset the jump timeout timer_jumpTimeoutDelta = JumpTimeout;// fall timeoutif (_fallTimeoutDelta >= 0.0f){_fallTimeoutDelta -= Time.deltaTime;}else{// update animator if using characterif (_hasAnimator){_animator.SetBool(_animIDFreeFall, true);}}// if we are not grounded, do not jump_input.jump = false;}// apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)if (_verticalVelocity < _terminalVelocity){_verticalVelocity += Gravity * Time.deltaTime;}}private static float ClampAngle(float lfAngle, float lfMin, float lfMax){if (lfAngle < -360f) lfAngle += 360f;if (lfAngle > 360f) lfAngle -= 360f;return Mathf.Clamp(lfAngle, lfMin, lfMax);}}}
与模型控制通信脚本,模型控制脚本监听该脚本数值变化
using UnityEngine;#if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKEDusing UnityEngine.InputSystem;#endifnamespace StarterAssets{public class StarterAssetsInputs : MonoBehaviour{[Header("Character Input Values")]public Vector2 move;public Vector2 look;public bool jump;public bool sprint;[Header("Movement Settings")]public bool analogMovement;#if ENABLE_INPUT_SYSTEM && STARTER_ASSETS_PACKAGES_CHECKEDpublic void OnMove(InputValue value){MoveInput(value.Get<Vector2>());}public void OnLook(InputValue value){if(cursorInputForLook){LookInput(value.Get<Vector2>());}}public void OnJump(InputValue value){JumpInput(value.isPressed);}public void OnSprint(InputValue value){SprintInput(value.isPressed);}#else// old input sys if we do decide to have it (most likely wont)...#endifpublic void MoveInput(Vector2 newMoveDirection){move = newMoveDirection;} public void LookInput(Vector2 newLookDirection){look = newLookDirection;}public void JumpInput(bool newJumpState){jump = newJumpState;}public void SprintInput(bool newSprintState){sprint = newSprintState;}}}
相机控制脚本,控制朝向及固定位置跟随
using System;using System.Collections;using System.Collections.Generic;using UnityEngine;/*create by keefor on 2021/11/18*/public class TouchCamera : MonoBehaviour{ //public bool canScale = true; public Range angleRange = new Range(0, 90); //around center public Transform target; private Transform camtra; private bool m_isSinleFinger; private Vector2 targetAngles; private Vector2 currentAngles; private float currentDistance; // private Vector3 lastPos; //private Vector3 newPos; //Damper(阻尼) for move and rotate [Range(0, 10)] public float damper = 8; void Start() { //newPos = transform.position; camtra = Camera.main.transform; // lastPos = target.position; currentAngles = targetAngles = camtra.eulerAngles; currentDistance = Vector3.Distance(camtra.position, target.position); } public void TouchDown(Vector2 pos) { currentAngles = targetAngles = camtra.eulerAngles; currentDistance = Vector3.Distance(camtra.position, target.position); } public void TouchDrag(Vector2 pos) { targetAngles.y += pos.x; targetAngles.x -= pos.y; targetAngles.x = Mathf.Clamp(targetAngles.x, angleRange.min, angleRange.max); currentAngles = Vector2.Lerp(currentAngles, targetAngles, damper * Time.deltaTime); //rotate of target // Quaternion rotation = Quaternion.Euler(currentAngles); // Vector3 newPosition = target.position + rotation * Vector3.back * currentDistance; // camtra.position = newPosition; // camtra.rotation = rotation; } void LateUpdate() { if (camtra == null) return;// Debug.Log("设置相机位置和旋转"); Quaternion rotation = Quaternion.Euler(currentAngles); Vector3 newPosition = target.position + rotation * Vector3.back * currentDistance; camtra.position = newPosition; camtra.rotation = rotation; // var delte = target.position- lastPos ; // lastPos = target.position; // camtra.position += delte; }}[Serializable]public struct Range{ public float min; public float max; public Range(float min, float max) { this.min = min; this.max = max; }}