找回密码
 立即注册
查看: 298|回复: 0

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

[复制链接]
发表于 2022-7-12 10:01 | 显示全部楼层 |阅读模式
示意


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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 05:28 , Processed in 0.088491 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表