明天实现的内容:
新增锁定输出
为输出模块增加锁定键和锁定信号,更新信号。
--- IPlayerInput public bool lockOn; //锁定信号 --- JoystickInput public MyButton buttonLockOn = new MyButton(); //锁定键 // Update is called once per frame void Update() { // 更新按键 buttonLockOn.Tick(Input.GetButton(btnRS)); // 锁定信号 lockOn = buttonLockOn.onPressed;
锁定和解锁的代码逻辑和摄像机代码逻辑
首先,要锁定目标,先要确定要锁定的指标是什么。咱们应用Physics.OverlapBox来失去指定盒子区域内的碰撞体。
锁定须要始终将摄像机对准目标。所以咱们在CameraController中增加新办法LockOn_or_Unlock用来解决锁定解锁逻辑,而后在PlayerController中调用该办法。
// 摄像机锁定/解除锁定 public void LockOn_or_Unlock() { // 尝试去锁定一个 Vector3 tmp_modelCenter = modelGO.transform.position + Vector3.up; //取得模型的核心 Vector3 tmp_boxCenter = tmp_modelCenter + modelGO.transform.forward * 5.0f; //失去OverlapBox的核心 Collider[] cols = Physics.OverlapBox(tmp_boxCenter, //通过OverlapBox尝试获取范畴内Enemy标签的碰撞体 new Vector3(1.0f, 1.0f, 5.0f), modelGO.transform.rotation, LayerMask.GetMask("Enemy")); if (cols.Length != 0 && lockTarget == null) { //如果失去碰撞体并且没有锁定目标 将第一个赋值给lockTarget lockTarget = cols[0].gameObject; lockonIcon.enabled = true; // 设置LockonIcon的图片地位 lockonIcon.rectTransform.position = Camera.main.WorldToScreenPoint(lockTarget.transform.position); isLockon = true; } else { // 如果没有检测到任何货色或者曾经锁定了指标 将lockTarget设置为null lockTarget = null; lockonIcon.enabled = false; isLockon = false; } }
LockOn_or_Unlock会应用OverlapBox查看给定范畴内是否有碰撞体并且是否曾经锁定目标,如果找到了碰撞体,将数组中的第一个给lockTarget,如果没找到或曾经锁定了指标则设置为null。
如果摄像机没有锁定,则在FixedUpdate中将playerController按输出设置旋转。如果锁定了,则摄像机要计算模型和锁定目标的方向向量,把这个方向向量交给摄像机,在咱们的架构中,间接用来设置PlayerController的forward就行,记得要将该向量的y轴设置为0。最初,咱们会在锁定时让摄像机始终看向指标的“脚底”来让视角更贴近黑魂。
void FixedUpdate() { if (lockTarget == null) //如果没有锁定目标 按输出管制PlayerController旋转 { // 失去摄像机旋转前的模型欧拉角 Vector3 temp_modelEuler = modelGO.transform.eulerAngles; // 左右旋转时间接旋转PlayerHandle 摄像机也会跟着 playerController.transform.Rotate(Vector3.up, current_pi.cameraRight * horizontalSensitivity * Time.fixedDeltaTime); // 摄像机旋转后将模型原来的欧拉角再赋给模型 保障模型不动 modelGO.transform.eulerAngles = temp_modelEuler; // 高低旋转时旋转CameraHandle temp_eulerX -= current_pi.cameraUp * verticalSensitivity * Time.fixedDeltaTime; // 限度俯仰角 temp_eulerX = Mathf.Clamp(temp_eulerX, -40, 30); // 赋值localEulerAngles cameraHandle.transform.localEulerAngles = new Vector3(temp_eulerX, 0, 0); } else //如果锁定了指标 计算从模型到锁定目标的方向向量 将PlayerController的forward设置为该向量 { // 计算方向向量 Vector3 temp_forward = lockTarget.transform.position - modelGO.transform.position; // 将向量的y轴设置为0 PlayerController的Y轴不须要旋转 temp_forward.y = 0; // 设置PlayerController的forward playerController.transform.forward = temp_forward; // 锁定摄像机看指标的“脚底” cameraHandle.transform.LookAt(lockTarget.transform.parent.position); } // 摄像机的地位通过SmoothDamp来实现一种提早挪动的成果 cameraGO.transform.position = Vector3.SmoothDamp( cameraGO.transform.position, this.transform.position, ref temp_dampValue, 0.1f * current_pi.dirMag); // 让摄像机放弃看向一个地位 避免地位进行SmoothDamp时的抖动 cameraGO.transform.LookAt(cameraHandle.transform); }
锁定的提醒UI
为了在游戏中提醒玩家以后锁定了哪个指标,咱们要退出一个UI。
须要在没有进行锁定时将其enabled设置为false。只在锁定时将其设置为true。最初将UI的地位放到锁定的对象身上。
private void Update() { if(isLockon) { // 更新LockonIcon的图片地位 lockonIcon.rectTransform.position = Camera.main.WorldToScreenPoint(lockTarget.transform.position); } }
锁定的控制器代码逻辑
锁定时控制器中的代码和摄像机相似,一个是设置摄像机对准目标,一个是设置模型对准目标。只有当模型对准目标,咱们能力引入锁定时相应的动画。同样重要的还有锁定状态下的挪动计算,因为锁定时模型和PlayerController的方向都已锁死,此时方向的判断不依据模型了,而是间接来自输出的产生方向,咱们在输出模块中用dirVec来示意。
// -- PlayerController -- // Update is called once per frame void Update() { // ... // 触发锁定 if (current_pi.lockOn) { camCon.LockOn_or_Unlock(); } if (camCon.isLockon) //摄像机已锁定 将模型旋转设定为朝向指标 { // 因为曾经在CameraController设置了PlayerController对象的旋转所以间接给模型就行 model.transform.forward = this.transform.forward; // 计算锁定时的挪动 if (!lockPlanar) { m_planarVec = current_pi.dirVec * walkSpeed * (current_pi.run ? runMultiplier : 1.0f); } } else //摄像机没有锁定 依据输出管制模型旋转 { // 只在有速度时可能旋转 避免原地旋转 if (current_pi.dirMag > 0.1f) { // 使用旋转 应用Slerp进行成果优化 model.transform.forward = Vector3.Slerp(model.transform.forward, current_pi.dirVec, 0.3f); } // 计算没有锁定时的挪动量 if (!lockPlanar) { m_planarVec = current_pi.dirMag * model.transform.forward * walkSpeed * (current_pi.run ? runMultiplier : 1.0f); } } }
总结一下,因为咱们程度旋转摄像机是靠旋转PlayerController游戏对象来实现,所以锁定目标将PlayerController游戏对象的forward指向指标就行。同样的,因为曾经设置了PlayerController在锁定时指向指标,要将模型指向指标只须要将PlayerController对象的forward给模型就行。挪动的方向判断须要通过输出产生的方向,也就是咱们之前做输出模块时写的dirVec来判断。
锁定的动画
之前说要将模型始终对准目标能力引入锁定对应的动画,锁定对应的动画就是始终朝向一个方向时的后退后退和左右侧步。要退出这些动画,咱们须要将ground混合树批改为2D Freeform类型。原来的挪动仍然只由forward参数操控,咱们要额定退出right参数,在锁定时,咱们将通过批改right参数操控左右侧步,以及调整forward为负值来操控后退。这样还不须要退出新混合树就能够实现原来的挪动和锁定时的挪动了。
至于动画参数的代码,构造和之前的逻辑相似,在锁定状态下要调整的是forward和right,非锁定状态下只调整forward就行。具体代码如下:
// Update is called once per frame void Update() { // --------------------- 动画参数 --------------------- if (camCon.isLockon) { Vector3 localDirVec = this.transform.InverseTransformDirection(current_pi.dirVec); anim.SetFloat("forward", localDirVec.z * ((current_pi.run) ? 2.0f : 1.0f)); anim.SetFloat("right", localDirVec.x * ((current_pi.run) ? 2.0f : 1.0f)); } else { anim.SetFloat("forward", current_pi.dirMag * Mathf.Lerp(anim.GetFloat("forward"), (current_pi.run) ? 2.0f : 1.0f, 0.5f)); anim.SetFloat("right", 0); } // ... }
锁定状态下的翻滚和跳跃
因为锁定状态下模型方向判断和之前不一样了,所以咱们须要从新设计锁定状态下的翻滚和后跳。设计方案是,设置一个新的bool值trackDirection,当该bool为true时,将追踪m_planarVec,提供给翻滚后跳作为方向。当咱们开始跳跃或翻滚时,将trackDirection设置为true。落地时设置为false。
// 进入Base层的动画节点roll时执行的办法 // 通过PlayerController动画机中的roll节点上挂载的FSMOnEnter调用 public void OnRollEnter() { // 敞开输出模块 current_pi.inputEnabled = false; // 锁定平台挪动计算 lockPlanar = true; // 使用翻滚冲量 m_planarVec = m_planarVec.normalized * rollThrust; // 追踪dirVec trackDirection = true; }
当锁定时,要旋转模型的时候,如果trackDirection为true,则依照m_planarVec作为方向。
// --------------------- 模型旋转和位移 --------------------- if (camCon.isLockon) //摄像机已锁定 将模型旋转设定为朝向指标 { if(trackDirection) //以后是否追踪方向 { model.transform.forward = m_planarVec.normalized; } else { // 因为曾经在CameraController设置了PlayerController对象的旋转所以间接给模型就行 model.transform.forward = this.transform.forward; }
最初,将动画机调整一下,将right参数思考进翻滚和跳跃。
主动解除锁定
到目前为止,咱们的锁定性能根本刊用了,然而每当玩家想要解除锁定时必须再次按下锁定键,否则无论跑出多远都会锁定到指标上,从游戏设计的角度来讲,玩家跑出很远的间隔个别都是心愿勾销锁定的,而且如果在很远距离都能锁定敌人会导致一些追踪魔法能打很远。所以最初咱们再做一个主动解除锁定性能。
通过计算玩家和指标的间隔,如果间隔大于某个值,则主动执行解锁。
private void Update() { // 如果锁定了指标 if(isLockon) { // 更新LockonIcon的图片地位 lockonIcon.rectTransform.position = Camera.main.WorldToScreenPoint(lockTarget.transform.position); // 超出某个间隔主动解除锁定 if (Vector3.Distance(lockTarget.transform.position, modelGO.transform.position) > 10f) { lockTarget = null; lockonIcon.enabled = false; isLockon = false; } } }