Unity制作RPG游戏的角色二段跳功能(不跳出地形外)

it2022-05-06  4

前言

制作RPG游戏的时候,一般我们会用寻路系统Navigation,假如要制作一个跳跃功能,需要注意跳跃的时候不能跳到地形外面,并且起跳的时候,要把NavMeshAgent关闭,落地的时候再重新激活,下面就用一个简单的例子教大家。

1 烘焙地形

选择地形,设置为Navigation Static

点击菜单Windwo/Navigation,在Navigation窗口的Bake标签页中点击Bake按钮,开始烘焙

2 角色控制

本例中用的是一个Cube代替角色,给Cube挂上NavMeshAgent组件 编写Player.cs脚本,按方向键控制Cube移动,按空白键控制Cube跳跃 代码如下

// Player.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class Player : MonoBehaviour { public NavMeshAgent navAgent; JumpLogic m_jumpLogic = new JumpLogic(); // Use this for initialization void Start () { m_jumpLogic.Init(navAgent, transform); } // Update is called once per frame void Update () { m_jumpLogic.UpdateJump(); if (Input.GetKeyDown(KeyCode.Space)) { m_jumpLogic.Jump(); } if (Input.GetKey(KeyCode.UpArrow)) { transform.position += new Vector3(0, 0, Time.deltaTime); } if (Input.GetKey(KeyCode.DownArrow)) { transform.position += new Vector3(0, 0, -Time.deltaTime); } if (Input.GetKey(KeyCode.LeftArrow)) { transform.position += new Vector3(-Time.deltaTime, 0, 0); } if (Input.GetKey(KeyCode.RightArrow)) { transform.position += new Vector3(Time.deltaTime, 0, 0); } } }

3 跳跃逻辑

编写跳跃逻辑脚本JumpLogic.cs,实现二段跳功能,并确保不会跳到地形外面,代码如下

// JumpLogic.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class JumpLogic { public enum JUMPTYPE { JUMP01 = 1, JUMP02 = 2 } private NavMeshAgent m_navAgent; private Transform m_modelTrans; private bool m_isJump; /// <summary> /// 是否处在跳跃中 /// </summary> public bool IsJump { get { return m_isJump; } } private bool mIsJump2; /// <summary> /// 是否处在二段跳中 /// </summary> public bool IsJump2 { get { return mIsJump2; } } private bool m_isJumpReady; private bool m_isJumpOver; private Vector3 m_targetPos; public float ShotSpeed = 6; public float Duration; //代表从起始点出发到目标点经过的时长 public float Gravity = -15; //重力加速度 private Vector3 mStartSpeed; //初速度向量 private float mUseTimeJump; private float mUseTimeJumpOver; private bool mIsSourceJump; //是否是原地起跳 private Vector3 mSourcePos; //起跳之前的位置 private float mfDisYOld; //上一次的当前与目标高度差 private int m_curStep; //记录当前几段跳 Vector3 m_targetDirection; //移动的目标点 private Vector3 m_vecTargetPos; public Vector3 VecTargetPos { get { return m_vecTargetPos; } set { m_vecTargetPos = value; } } private Vector3 Position { get { return m_modelTrans.position; } set { m_modelTrans.position = value; } } public void Init(NavMeshAgent navAgent, Transform modelTrans) { m_navAgent = navAgent; m_modelTrans = modelTrans; } public void Jump() { int nStep = 1; //默认想服务器申请使用一段跳,如果正在轻功状态并且二段跳已经可以使用并且未激活,则申请发起二段跳 if (IsJump && !IsJump2) { nStep = 2; } //如果没有使用摇杆则用人物当前的朝向 m_targetDirection = m_modelTrans.forward; bool bhit = false; NavMeshHit hit; Vector3 jumpPos = Vector3.zero; if (nStep == (int)JUMPTYPE.JUMP02) { //二段跳 jumpPos = Position + m_targetDirection * 10f; bhit = NavMesh.SamplePosition(jumpPos, out hit, 5, -1); if (!bhit) { jumpPos = Position + m_targetDirection * 10f; bhit = NavMesh.SamplePosition(jumpPos, out hit, 12, -1); } //先计算如果两个点离的很近就走原地跳跃 if (bhit) { float dis = Vector3.Distance(hit.position, Position); if (dis < 4) { bhit = false; mSourcePos = hit.position; } } else { //二段跳如果没有找到可跳跃点,则将原始点设置为一段跳的起始点,防止二段跳的时候又跳回去了 mSourcePos = m_targetPos; } } else { //一段跳 //先计算跳跃点 mSourcePos = Position; jumpPos = Position + m_targetDirection * 6f; bhit = NavMesh.SamplePosition(jumpPos, out hit, 3, 1); if (!bhit) { jumpPos = Position + m_targetDirection * 6f; bhit = NavMesh.SamplePosition(jumpPos, out hit, 5, 1); } //先计算如果两个点离的很近就走原地跳跃 if (bhit) { float dis = Vector3.Distance(hit.position, Position); //Debugger.Log("跳跃目标距离:{0}", dis); if (dis < 4) { bhit = false; mSourcePos = hit.position; } } } //跳跃朝向 Vector3 dir = Vector3.zero; var isYuanDi = true; if (!bhit) { //原地跳跃的处理 jumpPos = mSourcePos; dir = m_targetDirection;//原地起跳直接用摇杆方向 isYuanDi = true; } else { //可正常跳跃 jumpPos = hit.position; isYuanDi = false; } JumpToOther(jumpPos, nStep, isYuanDi); } public void JumpToOther(Vector3 dstPos, int nStep, bool isYuanDi) { if (mIsJump2 || m_isJumpOver) { //已经处在二段跳或者跳跃结束了就不操作了 return; } m_curStep = nStep; m_targetDirection = m_modelTrans.forward; if (nStep == (int)JUMPTYPE.JUMP02) { //二段跳 mIsJump2 = true; ShotSpeed = 9; } else { //一段跳 ShotSpeed = 7; } Vector3 dir = Vector3.zero; if (isYuanDi) { //原地跳 dir = m_targetDirection;//原地起跳直接用摇杆方向 mIsSourceJump = true; if (nStep == (int)JUMPTYPE.JUMP02) { Duration = .8f; ShotSpeed = 7f; } else { Duration = .6f; ShotSpeed = 5f; } } else { //正常跳 Duration = Vector3.Distance(Position, dstPos) / ShotSpeed; dir = (new Vector3(dstPos.x, Position.y, dstPos.z) - Position);//正常跳跃用目标点的朝向 } m_isJumpReady = true; m_isJump = true; //IsLightState = true; m_isJumpOver = false; m_navAgent.enabled = false; m_targetPos = dstPos; //计算初速度 mStartSpeed = new Vector3( (m_targetPos.x - Position.x) / Duration, (m_targetPos.y - Position.y) / Duration - 0.5f * Gravity * Duration, (m_targetPos.z - Position.z) / Duration ); mUseTimeJump = 0; //播放起跳动作 if (mIsJump2) { //TODO 播放二段跳动作 } else { //TODO 播放起跳动作 } //立即朝向 dir.Normalize(); FaceToDirection(dir); m_isJumpReady = false; } /// <summary> /// 面朝方向向量 /// </summary> /// <param name="_dir"></param> public void FaceToDirection(Vector3 dir) { if (dir == Vector3.zero) { return; } m_modelTrans.rotation = Quaternion.LookRotation(dir); } public void UpdateJump() { if (m_isJump && !m_isJumpReady) { if (!m_isJumpOver) { //跳跃中处理 Vector3 deltaSpeed = Vector3.zero; deltaSpeed.y = Gravity * (mUseTimeJump + Time.deltaTime);//v=at Position += (mStartSpeed + deltaSpeed) * Time.deltaTime; float disY = Mathf.Abs(Position.y - m_targetPos.y); float remainTime = Duration - mUseTimeJump; //Debugger.Log("disY:{0}", disY); //是否已达到目标位置 if ((remainTime <= .1f && (disY <= .2f || disY > mfDisYOld)) || (!mIsSourceJump && mUseTimeJump >= Duration)) { //跳跃结束,更新目标点,否则在寻路过程中跳跃,结束时会播放跑步动作 m_vecTargetPos = Position; m_isJumpOver = true; mUseTimeJumpOver = 0; mIsJump2 = false; m_navAgent.enabled = true; //TODO 播放跳跃结束动作 // } else { mUseTimeJump += Time.deltaTime; mfDisYOld = disY;//记录上一次的高度,防止卡帧时的处理 } } else { //跳跃结束延时处理,防止一下落就行走看不到结束动画 mUseTimeJumpOver += Time.deltaTime; if (mUseTimeJumpOver >= 0.1f) { //延时一下再结束 m_isJump = false; m_isJumpOver = false; mIsSourceJump = false; } } } } }

4 运行效果


最新回复(0)