Unity Arpg学习日志三:目标锁定

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、前置

(1)使用CinemachineFreeLook对操作的角色进行跟随,关于Cinemachine可参考 【游戏开发教程】Unity Cinemachine快速上手,详细案例讲解(虚拟相机系统 | 新发出品 | 良心教程)
(2)相机的LookAt属性都使用角色及目标上挂在的LookRoot,可参考我的偏移
在这里插入图片描述
此外还要给每个对象添加CapsuleCollider


提示:以下是本篇文章正文内容,下面案例可供参考

二、在相机前方搜寻一定范围内的目标

在相机观察方向前方添加一个检测盒子,将检测到的目标存到targets里

在这里插入图片描述

这里使用协程循环是因为防止当目标不在摄像机检测范围内时依然可以锁定

 IEnumerator OnLockTarget()
 {
     while(true)
     {
         var viewDir = Camera.main.transform.forward.normalized;
         boxCenter = transform.position + viewDir * boxOffset.x + transform.up * boxOffset.y;
         targets = Physics.OverlapBox(boxCenter, size, transform.rotation, layer);
         yield return findTargetWaitTime;
     }
 }

这段代码可在scene绘制出检测盒子

#if UNITY_EDITOR
    private void OnDrawGizmos()
    {
        Gizmos.DrawWireCube(boxCenter, size*2);
    }
#endif

在这里插入图片描述

三、切换锁定目标

可以自定自定义按键,切换目标

void OnSwitchLockTarget(int up=0)
{
    if(targets.Length == 0) return;
    index += up;
    if(index >= targets.Length) index = 0;
    else if (index <0) index=targets.Length-1;

    targetLootRoot=targets[index].transform.Find("LookRoot");
    theLockTarget = targets[index].transform;


    //cm.m_LookAt = target;
    cm.LookAt = targetLootRoot;

    
}

四、移动范围限制

在这里插入图片描述

有时角色会离开相机,这里我加了一个基于屏幕宽高的比例的判定,在适当时机移动相机,不让角色离开摄像机

 void CmClamp()
 {
     if (Camera.main.WorldToScreenPoint(transform.position).x <= Screen.width / CameraLimit)
             cm.m_XAxis.Value += 1f;
     if (Camera.main.WorldToScreenPoint(transform.position).x >= Screen.width / CameraLimit * 3)
             cm.m_XAxis.Value -= 1f;
 }

CameraLimit设置在 3~5 即可

总结

将脚本挂在要操控的角色身上,参数设置如下
在这里插入图片描述

在这里插入图片描述

 using Cinemachine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class LockTarget : MonoBehaviour
{
    [SerializeField]
    CinemachineFreeLook cm;

    



    Transform theLockTarget;//目标
    Transform targetLootRoot;//目标的LootRoot


    bool isLock;
    int index=0;

    public float MaxDistance;


    
    Vector3 boxCenter;//检测盒子中心
    public LayerMask layer;//检测层级
    public Vector2 boxOffset;//盒子产生时的位置偏移
    public  Vector3 size= new Vector3(2.0f, 2.0f, 2.0f);//盒子的大小
    WaitForSeconds findTargetWaitTime;//每次搜索间隔
    public float waitTime;//间隔事件
    Collider[] targets;//检测到的物体

    public Image lookPoint;

    public float CameraLimit;

    
    
    
    
    private void Start()
    {
        findTargetWaitTime=new WaitForSeconds(waitTime);
    }

    private void Update()
    {
        InputCheck();

        LookPointFlow();
        
        LostTarget();

        CmClamp();  
    }

    //开始锁定
    void OnLock()
    {
        lookPoint.gameObject.SetActive(true);
       


        StartCoroutine(OnLockTarget());

        StartCoroutine(Delay(0.5f));

        OnSwitchLockTarget(0);

        isLock = true;
    }

    //解除锁定
    void UnLock()
    {
        StopCoroutine(OnLockTarget());
        
        cm.m_LookAt=transform.Find("LookRoot");
        isLock = false;
        lookPoint.gameObject.SetActive(false);  
    }

    
    //切换目标
    void OnSwitchLockTarget(int up=0)
    {
        
        if(targets.Length == 0) return;
        index += up;
        if(index >= targets.Length) index = 0;
        else if (index <0) index=targets.Length-1;

        targetLootRoot=targets[index].transform.Find("LookRoot");
        theLockTarget = targets[index].transform;


        //cm.m_LookAt = target;
        cm.LookAt = targetLootRoot;

        
    }

    void InputCheck()
    {
        if (Input.GetKeyDown(KeyCode.G))
        {
            if (isLock)
            {
                UnLock();

            }

            else
            {
                OnLock();

            }
        }
        if (isLock)
        {
            if (Input.GetKeyDown(KeyCode.LeftArrow)) OnSwitchLockTarget(1);
            else if (Input.GetKeyDown(KeyCode.RightArrow)) OnSwitchLockTarget(-1);
        }
    }

    //在摄像机前方范围内搜寻目标
    IEnumerator OnLockTarget()
    {
        while(true)
        {
            var viewDir = Camera.main.transform.forward.normalized;
            boxCenter = transform.position + viewDir * boxOffset.x + transform.up * boxOffset.y;
            targets = Physics.OverlapBox(boxCenter, size, transform.rotation, layer);
            yield return findTargetWaitTime;
        }
    }

    //延迟执行
    IEnumerator Delay(float delayTime)
    {
        yield return new WaitForSeconds(delayTime);
    }

    /// <summary>
    /// 防止player超出摄像机范围
    /// </summary>
    void CmClamp()
    {
        if (Camera.main.WorldToScreenPoint(transform.position).x <= Screen.width / CameraLimit)
                cm.m_XAxis.Value += 1f;
        if (Camera.main.WorldToScreenPoint(transform.position).x >= Screen.width / CameraLimit * 3)
                cm.m_XAxis.Value -= 1f;
    }

    /// <summary>
    /// 锁定图标追随目标
    /// </summary>
    void LookPointFlow()
    {
        if (!isLock || targetLootRoot == null) return;

        lookPoint.rectTransform.position = Camera.main.WorldToScreenPoint(targetLootRoot.position);
    }

    /// <summary>
    /// 距离过远时取消锁定或者切换目标
    /// </summary>
    void LostTarget()
    {
        if (!isLock||targetLootRoot==null) return;
        if(Vector3.Distance(transform.position, targetLootRoot.position)>=MaxDistance)
        {
            if(targets.Length!=0)
                OnSwitchLockTarget(1);

            else UnLock();
        }
            
    }
#if UNITY_EDITOR
    private void OnDrawGizmos()
    {
        Gizmos.DrawWireCube(boxCenter, size*2);
    }
#endif
}

补充-遮挡时切换目标-25.5.20

修改离目标过远时的函数,改为使用协程,防止重复检测
判断玩家与目标之间是否有障碍物,有则切换目标
在打开锁定时启用协程

 IEnumerator LostOrBlockTarget()
{
    
    while (true)
    {
        if (!isLock || targetLootRoot == null) yield return null;
        Debug.DrawLine(transform.position, targetLootRoot.position,Color.blue);
        if (Vector3.Distance(transform.position, targetLootRoot.position) >= MaxDistance|| 
                Physics.Raycast(transform.position, targetLootRoot.position - transform.position,
                    Vector3.Distance(transform.position, targetLootRoot.position), blockMask))        
        {
            
           
            if (targets.Length != 0)
                OnSwitchLockTarget(1);

            else UnLock();
        }
       
        yield return new WaitForSeconds(0.7f);
    }
        
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值