IT圈老男孩1 发表于 2022-7-12 10:01

Unity中,3D角色的移动(斜面上)、跳跃、冲刺

示意


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{        private Rigidbody _rb;    public bool IsInDash{ get; private set; }        private float _jumpForce = 15f;    // 与地面之间的高度大于该值时,认为在空中        private float _checkInAirDistance = 0.6f;        private float _moveSpeed = 5f;        private float _dashMaxTime = 0.1f;        private float _dashMaxDistance = 6f;        private float _gravityFactorWhenRise = 1f;        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{        private Transform _target;    private Vector3 _offset;    public bool IsInFollow { get; private set; }        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);    }}
页: [1]
查看完整版本: Unity中,3D角色的移动(斜面上)、跳跃、冲刺