添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
快乐的麻辣香锅  ·  typescript ...·  1 周前    · 
高大的毛衣  ·  错误:cannot find ...·  3 月前    · 
帅气的猴子  ·  MySQL :: MySQL 8.0 ...·  1 年前    · 
本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议 》和 《 阿里云开发者社区知识产权保护指引 》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单 进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

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

 >>>>>>经测试,上述方案并不是很好用,会碰到以下几个问题:

(1)角色在Obstacle周围挤来挤去,行为很诡异;

(2)通过不断地靠近Obstacle,当遇到卡顿的时候,角色会穿透阻挡物;

(3)Obstacle虽然可以设置Cave属性,也就是动态切割导航面,但由于一些原因,动态切割的效果非常差,尤其是在一些不平的地面部分更是如此。

基于这些思考,推荐使用如下新的方法来做阻挡效果:

  通过NavMesh的Layer来实现:

  通过动态改变NavMeshAgent所能使用的层(NavMeshWalkable),来实现双向和单向阻挡的效果,经验证这种方案表现效果比较好,只是在场景制作时就必须确定不同层区域的划分。

  上述方案再配合一些魔法墙之类的特效,总体来说表现效果还是不错的,不过代码逻辑一定要清晰。

第二步 配置标准acl 指定需要转换的多个内部主机地址 access-list 编号 permit 网段地址 子网掩码反码 第三步 配置 转换后的公网地址地址池 ip nat pool 地址池名字 起始地址 结束地址 子网掩码 第四步 配置动态地址 haeu2qzuufd3q