|
示意
3d角色移动.gif
说明
可控制角色在xoz平面内移动。当处于斜面上时,可沿斜面移动。
地面(包括斜坡)的Layer需设置为Ground。
image.png
角色的Rigidbody的x和z向的旋转需锁住。
image.png
可控制角色从地面上跳起、向前冲刺、向下冲刺。
代码
角色移动:PlayerMoveCtr
using System;using System.Collections;using System.Collections.Generic;using UnityEngine;public class PlayerMoveCtr : MonoBehaviour{ [SerializeField] private Rigidbody _rb; public bool IsInDash{ get; private set; } [SerializeField] private float _jumpForce = 15f; // 与地面之间的高度大于该值时,认为在空中 [SerializeField] private float _checkInAirDistance = 0.6f; [SerializeField] private float _moveSpeed = 5f; [SerializeField] private float _dashMaxTime = 0.1f; [SerializeField] private float _dashMaxDistance = 6f; [SerializeField] private float _gravityFactorWhenRise = 1f; [SerializeField] private float _gravityFactorWhenFall = 2f; private int GroundLayerMask { get { return 1 << LayerMask.NameToLayer("Ground"); } } private Vector2 _moveDir; public event Action<bool> MoveInXZEvent; public event Action DashDownEvent; public event Action DashForwardEvent; private void Start() { Init(); } private void Update() { AdjustForwardDir(); AdjustGravity(); } private void FixedUpdate() { Move(_moveDir); } private void OnDestroy() { Destroy(); } public void Init() { } public void Destroy() { } public void MoveInXZ(Vector2 dir) { _moveDir = dir; } private void Move(Vector2 dir) { if (!IsInDash && dir != Vector2.zero) { transform.forward = (new Vector3(dir.x, 0, dir.y)).normalized; AdjustForwardDir(); _rb.MovePosition(transform.position + transform.forward * _moveSpeed * Time.fixedDeltaTime); MoveInXZEvent?.Invoke(true); } } public void StopMoveInXZ() { _moveDir = Vector2.zero; MoveInXZEvent?.Invoke(false); } private void AdjustForwardDir() { var planeNormalDir = Vector3.up; if (Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, 1, GroundLayerMask)) { planeNormalDir = hit.normal; } var forward = Vector3.ProjectOnPlane(transform.forward, planeNormalDir); transform.rotation = Quaternion.LookRotation(forward, planeNormalDir); } private void AdjustGravity() { float gravityFactor = 0; if (_rb.velocity.y > 0) { gravityFactor = _gravityFactorWhenRise; } else if (_rb.velocity.y < 0) { gravityFactor = _gravityFactorWhenFall; } if (gravityFactor != 0) { _rb.AddForce(Vector3.up * gravityFactor * Physics.gravity.y, ForceMode.Force); } } private void StopMove() { _rb.velocity = Vector3.zero; _rb.angularVelocity = Vector3.zero; } public void MoveInYDir() { if (!IsInDash) { if (IsInAir(out float height)) { // 快速下冲 DashDown(height); } else { Jump(); } } } private void DashDown(float distance) { StartCoroutine(DashCoroutine(transform.up * -1, distance, _dashMaxTime)); DashDownEvent?.Invoke(); } private void Jump() { _rb.AddForce(Vector3.up * _jumpForce, ForceMode.Impulse); } public void DashForward() { if (!IsInDash) { StartCoroutine(DashCoroutine(transform.forward, _dashMaxDistance, _dashMaxTime)); DashForwardEvent?.Invoke(); } } IEnumerator DashCoroutine(Vector3 dir, float maxDistance, float maxDuration) { IsInDash = true; var beginPos = transform.position; var endPos = beginPos + dir * maxDistance; if (Physics.Linecast(beginPos, endPos, out RaycastHit hitInfo, GroundLayerMask)) { endPos = hitInfo.point; } float distance = Vector3.Distance(beginPos, endPos); float duration = distance / maxDistance * maxDuration; float timer = 0f; while (timer < duration) { _rb.MovePosition(Vector3.Lerp(beginPos, endPos, timer / duration)); yield return null; timer += Time.deltaTime; } _rb.MovePosition(endPos); IsInDash = false; StopMove(); } // 距离地面的高度为 public bool IsInAir(out float height) { height = Mathf.Infinity; var hits = Physics.RaycastAll(transform.position, transform.up*-1, 100, GroundLayerMask); foreach (var hit in hits) { // 是否是地面 if (hit.distance < height) { height = hit.distance; } } return height > _checkInAirDistance; }}
角色移动控制(当前使用键盘):PlayerMoveKeyboardInput
using System.Collections;using System.Collections.Generic;using UnityEngine;public class PlayerMoveKeyboardInput : MonoBehaviour{ private PlayerMoveCtr _ctr; private void Start() { Init(FindObjectOfType<PlayerMoveCtr>()); } private void Update() { var x = Input.GetAxis("Horizontal"); var y = Input.GetAxis("Vertical"); var moveDir = (new Vector2(x, y)); if (moveDir != Vector2.zero) { _ctr.MoveInXZ(moveDir.normalized); } else { _ctr.StopMoveInXZ(); } if (Input.GetKeyDown(KeyCode.Space)) { _ctr.MoveInYDir(); } if (Input.GetKeyDown(KeyCode.Q)) { _ctr.DashForward(); } } private void OnDestroy() { Destroy(); } public void Init(PlayerMoveCtr ctr) { _ctr = ctr; } public void Destroy() { _ctr = null; }}
相机跟随:CamCtr
using System.Collections;using System.Collections.Generic;using UnityEngine;public class CamCtr : MonoBehaviour{ [SerializeField] private Transform _target; private Vector3 _offset; public bool IsInFollow { get; private set; } [SerializeField] private float _moveSpeed = 10; private Vector3 TargetPos { get { return _target.position - _offset; } } private void Start() { Init(); } private void FixedUpdate() { if (IsInFollow) { Follow(); } } private void OnDestroy() { Destroy(); } public void Init() { _offset = _target.position - transform.position; StartFollow(); } public void Destroy() { } public void StartFollow() { IsInFollow = true; } public void StopFollow() { IsInFollow = false; } private void Follow() { transform.position = Vector3.Lerp(transform.position, TargetPos, _moveSpeed * Time.fixedDeltaTime); }} |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|