NavMesh系统动态碰撞的探讨

  Unity3D提供的NavMesh系统可以方便的解决游戏的寻路问题,但是该系统有一个比较让人不理解的问题:

  NavMesh导航时会忽略Physics系统本身的碰撞,也就是说NavMeshAgent在移动的过程中不会被Collider阻挡,而是会直接走过去(但是OnTriggerEnter等触发功能正常)。

  动态碰撞的功能对很多游戏都是一个基本的需求,而根据NavMesh提供的接口,唯一可以实现阻挡功能的只有NavMeshObstacle,而NavMeshObstacle只有一种形状:圆柱体,而且up方向固定,不能调整为侧向。总结起来就是以下几点:

  (1)导航网格的行走/碰撞区域只能预烘焙;

  (2)动态碰撞体只能通过挂载NavMeshObstacle组件来实现;

  (3)碰撞体的形状只有一种——圆柱体,严格来说就是圆形,而且是正圆还不能是椭圆。

  所以说到这里,基本上可以放弃使用各种形状的Collider来制作场景阻挡物了。不过,替代方案也还是有的:如果一定要使用Unity3D提供的NavMesh来做导航,那么可以将圆作为基本元素来模拟其它形状。

  

  上图展示了通过NavMeshObjstacle来模拟立方体阻挡物,为了方便的编辑该立方体的大小,可以写一个辅助脚本来实现:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[ExecuteInEditMode]
public class MultiObstacleHelper : MonoBehaviour
{
    public float Interval = 1f;         // Obstacle之间的间隔
    public int Num = 1;                 // Obstacle的个数

    private float curInterval = 1f;
    private int curNum = 1;

    private Transform template = null;

    void Awake()
    {
        template = gameObject.transform.Find("Obstacle");
    }

    void Start()
    {
        Adjust();
    }

    void Update()
    {
        if (Num <= 0) Num = curNum;
        Adjust();
    }

    private void Adjust()
    {
        if (template == null) return;
        AdjustInterval(AdjustNum());
    }

    private bool AdjustNum()
    {
        if (curNum == Num) return false;

        if (Num > curNum)
        {
            for (int i = 0; i < Num - curNum; ++i)
            {
                GameObject go = GameObject.Instantiate(template.gameObject) as GameObject;
                go.transform.parent = template.parent;
                go.transform.localPosition = Vector3.zero;
                go.transform.localScale = Vector3.one;
                go.transform.localRotation = Quaternion.identity;
            }
        }
        else if (Num < curNum)
        {
            int count = curNum - Num;
            List<Transform> lst = new List<Transform>();for (int i = 0; i < template.parent.transform.childCount; ++i)
            {
                if (count <= 0) break;
                if (template.parent.GetChild(i) != template)
                {
                    lst.Add(template.parent.GetChild(i));
                    count--;
                }
            }
            while(lst.Count > 0)
            {
                Transform tran = lst[0];
                GameObject.DestroyImmediate(tran.gameObject);
                lst.RemoveAt(0);
            }
            lst.Clear();
        }

        curNum = Num;
        return true;
    }

    private void AdjustInterval(bool numChange)
    {
        if (numChange == false && curInterval == Interval)
            return;

        int half = Num / 2;
        int index = 0;
        foreach (Transform tran in template.parent.gameObject.transform)
        {
            // 奇数个
            if (Num % 2 == 1)
            {
                Vector3 pos = tran.localPosition;
                pos.x = (index - half) * Interval;
                tran.localPosition = pos;
            }
            else
            {
                Vector3 pos = tran.localPosition;
                pos.x = (index - half + 0.5f) * Interval;
                tran.localPosition = pos;
            }
            index++;
        }

        curInterval = Interval;
    }

}

  上述代码可以调整Obstacle的个数和间距,然后再配合调整缩放比例基本上可以做出各种尺寸的立方体。

  单向阻挡的实现,可以通过组合Trigger和NavMeshObstacle来实现一个单向阻挡的功能:

  

  实现思路是当角色进入红色Trigger区域时,将后面的阻挡物隐掉,过1秒之后再激活,这样就可以实现一个单向阻挡物的功能,实现的代码比较简单,如下面所示:

using UnityEngine;
using System.Collections;

#if UNITY_EDITOR
using UnityEditor;
#endif

public class SinglePassTrigger : MonoBehaviour
{
    [HideInInspector]
    public Transform Object = null;
    public Transform Collider = null;
    public float PassTime = 1f;

    void Start()
    {
        Object = transform.parent.transform.Find("Object");
        Collider = transform.parent.transform.Find("Collider");
    }

    protected virtual void OnTriggerEnter(Collider other)
    {
        StopCoroutine("LetPassCoroutine");
        StartCoroutine("LetPassCoroutine");
    }

    protected virtual void OnTriggerExit(Collider other)
    { }

    IEnumerator LetPassCoroutine()
    {
        SetPassState(true);
        float startTime = Time.time;
        while(Time.time < startTime + PassTime)
        {
            yield return null;
        }
        SetPassState(false);
    }

    private void SetPassState(bool value)
    {
        if (Collider == null) return;

        Collider.gameObject.SetActive(!value);
    }

#if UNITY_EDITOR
    void OnDrawGizmos()
    {
        // 设置旋转矩阵
        Matrix4x4 rotationMatrix = Matrix4x4.TRS(Vector3.zero, transform.rotation, Vector3.one);
        Gizmos.matrix = transform.localToWorldMatrix;
        // 在Local坐标原点绘制标准尺寸的对象
        Gizmos.color = new Color(1f, 0f, 0f, 0.8f);
        Gizmos.DrawCube(Vector3.zero, Vector3.one);
        Gizmos.color = Color.black;
        Gizmos.DrawWireCube(Vector3.zero, Vector3.one);
        Gizmos.DrawIcon(transform.position + Vector3.up, "ban.png");
    }
#endif

}

  

时间: 2024-10-07 06:29:56

NavMesh系统动态碰撞的探讨的相关文章

NavMesh动态碰撞

今天遇到一个问题,就是怎样处理一些动态的障碍物. NavMesh是能够躲避静态的障碍物.NavMeshObstacle的作用就是动态添加障碍. 可是有个问题,NavMeshObstacle是圆,连椭圆都不行,所以.仅仅好写一个附属脚本.用圆拼成矩形,就能够了. using UnityEngine; using System.Collections; public class NavMeshObstacleHelper : MonoBehaviour { //coordinate public f

Linux系统动态IP地址的获取和静态IP地址的配置

今天刚安装好虚拟机和Linux系统后做了以下实验在这实验中遇到的知识点和实验过程如下 #-  1.Broadcast 协议选项 [BCAST]BROADCAST 指定用于发送广播消息的 IP 地址.使用本地 IP 地址和子网掩码创建缺省广播地址.子网掩码指示哪部分 IP 地址识别网络哪部分识别主机 #-  2.dhclient获得IP地址用ifconfig -a命令查看 .etho表示1个网卡eth1表示2个网卡 .lo表示回环地址 #-  3.用vi打开配置文件命令如下 之后出现以下配置文件

【教育系统】《深度探讨》如何建设千万级网络教育在线学习支持系统?

1) 用户 建设开放性学习环境,用户的统一授权.认证.用户容量成为关键问题,用户通过认证/授权服务管理用户,达到统一的管理,并通过后台负载均衡管理,实现访问用户分流.资源访问分流,实现大用户量的管理. 建设支持超过千万人访问的一站式远程学习支持系统平台. 2) 资源 随着学习环境的建设和发展,资源的海量存储.分类细化已成为关键课题,系统通过虚拟化技术进行存储和相关运算,实现资源存储和调度,用以支撑不同学习过程和教学资源的存储隔离和应用共享. 3) 数据服务 定义和完善接口和服务内容,搭建集成环境

使用JSP/Servalet技术开发新闻发布系统------动态网页开发基础

什么是动态网页? 动态网页是指在服务器端运行的程序或者网页,它们会随不同客户.不同时间,返回不同的网页. 动态网页的特点? (1).交互性:即网页会根据用户的要求和选择而动态改变和响应.采用动态网页技术的网站可以实现与用户的交互功能. (2).自动更新:无需手动操作,便会自动生成新的页面,可以节省工作量. (3).随机性:即当不同的时间.不同的人访问同一网址时会产生不同的页面效果. 为什么需要动态网页? 静态网页不能真正与用户实现互动.静态网页的内容是固定的,当修改了网页中的元素的时候,都需要重

8-2-伙伴系统-动态存储管理-第8章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第8章  伙伴系统 - 边界标识法 ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? Status.h        相关测试数据下载  链接? 数据包      

Spring Cloud微服务系统下的数据一致性探讨

我想这个问题需要根据自己的系统具体架构来分别讨论,这边拿一个车联网的系统举例. 拆除GPS这个功能接口需要分几个步骤实现(不涉及数据更新的步骤略去了): ①更改GPS设备状态(设备管理服务) ②更改工单状态(工单管理服务) 假设调用拆除GPS这个接口的时候,由于各种原因②不能工作了. 架构1:普通的微服务架构 工单管理服务要调用设备管理服务的功能,用的是spring cloud的自己的restTemplate,这个很简单,加上普通的事务就可以了. 架构2:引入了消息中间件 这个时候,服务之间调用

Android6.0以上系统动态获取权限

动态权限的申请方法: 1.首先,需要在AndroidManifest.xml静态申请权限,否则无法动态申请权限: <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="an

Android 6.0系统动态请求系统相机权限

1 private static final int TAKE_PHOTO_REQUEST_CODE = 1; 2 3 public static String takePhoto(Context context, int requestCode) { 4 String filePath = ""; 5 if (ContextCompat.checkSelfPermission(context, 6 Manifest.permission.CAMERA) 7 != PackageMan

solaris系统动态查看swap的使用情况

[email protected] # [email protected] # prstat  -aPlease wait... PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/NLWP 22879 oracle     11G  273M cpu8     0    0   0:13:47 3.6% extract/6 18091 oracle     48G   48G sleep   59    0   0:1