Unity DOTS physics

六月ゝ 毕业季﹏ 提交于 2020-01-24 04:26:30

由于官方暂无文档,且为临时版本,随时可能更新,所以本文档内容皆为胡编乱造,造谣生事,事在人为,为所欲为,为所欲为,为所欲为,为所欲为,为所欲为

1. package环境

  • Entities
  • Hybrid.Renderer
  • Burst
  • Collections
  • Jobs
  • Mathematics
  • Unity Physics

2. GameObject转Entity

添加convertToEntity,原GameObject中自带的colliderrigidbody会自动转换为PhysicsShapePhysicsBody,但仍然建议手动移除后重新添加新的Physics脚本

3. Physics Debug和 物理引擎设置

新建一个空GO,并添加脚本: converToEntity,physicsStep,physicsDebugDisplay
在这里插入图片描述
UnityPhysics貌似存在些计算上的bug,如: 两个垂直放置的静止的GO,会发生位移(HavokPhysics中无该问题):
PhysicsBug

4. 鼠标点击与碰撞检测

碰撞检测关键类:CollisionWorld

namespace Unity.Physics
{
    public struct CollisionWorld : ICollidable, IDisposable, ICloneable
    {
        public CollisionWorld(int numBodies);

        public NativeSlice<RigidBody> Bodies { get; }
        public int NumBodies { get; set; }
        public float CollisionTolerance { get; }

        public Aabb CalculateAabb();
        public Aabb CalculateAabb(RigidTransform transform);
        public bool CalculateDistance(PointDistanceInput input);
        public bool CalculateDistance<T>(ColliderDistanceInput input, ref T collector) where T : struct, ICollector<DistanceHit>;
        public bool CalculateDistance(ColliderDistanceInput input, ref NativeList<DistanceHit> allHits);
        public bool CalculateDistance(ColliderDistanceInput input, out DistanceHit closestHit);
        public bool CalculateDistance(ColliderDistanceInput input);
        public bool CalculateDistance<T>(PointDistanceInput input, ref T collector) where T : struct, ICollector<DistanceHit>;
        public bool CalculateDistance(PointDistanceInput input, ref NativeList<DistanceHit> allHits);
        public bool CalculateDistance(PointDistanceInput input, out DistanceHit closestHit);
        public bool CastCollider(ColliderCastInput input, ref NativeList<ColliderCastHit> allHits);
        public bool CastCollider(ColliderCastInput input, out ColliderCastHit closestHit);
        public bool CastCollider(ColliderCastInput input);
        public bool CastCollider<T>(ColliderCastInput input, ref T collector) where T : struct, ICollector<ColliderCastHit>;
        public bool CastRay<T>(RaycastInput input, ref T collector) where T : struct, ICollector<RaycastHit>;
        public bool CastRay(RaycastInput input, ref NativeList<RaycastHit> allHits);
        public bool CastRay(RaycastInput input, out RaycastHit closestHit);
        public bool CastRay(RaycastInput input);
        public object Clone();
        public void Dispose();
        public bool OverlapAabb(OverlapAabbInput input, ref NativeList<int> allHits);
        public JobHandle ScheduleUpdateDynamicLayer(ref PhysicsWorld world, float timeStep, float3 gravity, int numThreadsHint, JobHandle inputDeps);
    }
}

4.1 准备工作

Physics Category Namesphysics检测与filter设置:

  1. 创建: Create -> DOTS -> Physics -> Physics Category Names
  2. 代码相关:
    hitFilter记录

  /// <summary>
/// hitFilter记录
/// </summary>
public struct MousePick : IComponentData
{

    /// <summary>
    /// 一般为 int.maxValue 
    /// </summary>
    public int belongTo;
    /// <summary>
    /// 碰撞过滤
    /// </summary>
    public int filterHit;

    public int filterPlane;
}



[RequiresEntityConversion]
public class CMousePick : MonoBehaviour, IConvertGameObjectToEntity
{


    public bool ignoreTrigger = false;
    //belongTo取最大
    //public int belongTo = 0;

    public int filterHit = 0;
    public int filterPlane = 0;
    public PhysicsCategoryNames categoryNames;
    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponentData(entity, new MousePick { 
           
            belongTo = int.MaxValue,
            filterHit = filterHit,
            filterPlane = filterPlane,
        });
    }
}

CMousePickEditor代码: 注意新建一个名为 editor 的文件夹

using UnityEditor;
[CustomEditor(typeof(CMousePick))]
public class CMousePickEditor : Editor
{

    CMousePick Instance;
    string[] names = new string[32];
    private void OnEnable()
    {
        Instance = target as CMousePick;

       GetNames();
    }

    private void GetNames()
    {
        if (Instance.categoryNames)
        {
            for (int i = 0; i < Instance.categoryNames.CategoryNames.Count; i++)
            {
                names[i] = (Instance.categoryNames.CategoryNames[i] != "") ? Instance.categoryNames.CategoryNames[i] : "(empty) " + i.ToString();
            }
        }
        else
        {
            for (int i = 0; i < names.Length; i++)
            {
                names[i] = "";
            }
        }
       
    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        EditorGUI.BeginChangeCheck();
        {
            if (Instance.categoryNames && names[0] != "")
            {
                //Instance.belongTo =EditorGUILayout.MaskField("belongTo", Instance.belongTo, names);
                Instance.filterHit = EditorGUILayout.MaskField("射线点击过滤", Instance.filterHit, names);
                Instance.filterPlane = EditorGUILayout.MaskField("Plane过滤", Instance.filterPlane, names);
            }
            else
            {
                EditorGUILayout.HelpBox("尚未导入物理碰撞种类名", MessageType.Error);
                GetNames();
            }
        }
        if (EditorGUI.EndChangeCheck())
        {
            EditorUtility.SetDirty(Instance);
        }
       
    }
}

  1. 创建Entity: 场景中新建一个空物体,添加convertToEntityCMousePick,并设置 filter

最终效果:在这里插入图片描述

4.2 CastRay射线检测

官方实例使用了IJob进行射线检测,但是我不想用,所以:


using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Systems;
using RaycastHit = Unity.Physics.RaycastHit;

namespace PhysicsCollisionUtiltity
{
    public class PhysicsUtiltity
	{

        static BuildPhysicsWorld m_BuildPhysicsWorldSystem;
        /// <summary>
        /// 获取过滤的同时完成Job
        /// </summary>
        /// <param name="jobSystem"></param>
        /// <param name="inputDependencies"></param>
        /// <param name="layerMask"></param>
        /// <returns></returns>
        static CollisionFilter GetFilter( ref JobHandle inputDependencies,int layerMask)
        {
            var filter = new CollisionFilter
            {
                BelongsTo = CollisionFilter.Default.BelongsTo,
                CollidesWith = (uint)layerMask,
                GroupIndex = CollisionFilter.Default.GroupIndex,
            };
            if (m_BuildPhysicsWorldSystem == null)
            {
                m_BuildPhysicsWorldSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<BuildPhysicsWorld>();
            }
            ///等待BuildPhysicsWorldSystem 和 依赖项完成后才获取射线数据
            inputDependencies = JobHandle.CombineDependencies(inputDependencies, m_BuildPhysicsWorldSystem.FinalJobHandle);
            inputDependencies.Complete();
            return filter;
        }
        public static bool Raycast(ref JobHandle inputDependencies, float3 start, float3 end,
   int layerMask = int.MaxValue)
        {
            var RayInput = new RaycastInput
            {
                Start = start,
                End = end,
                Filter = GetFilter(ref inputDependencies, layerMask),
            };
            return m_BuildPhysicsWorldSystem.PhysicsWorld.CollisionWorld.CastRay(RayInput);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="jobSystem">用于EntityPhysics世界获取</param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="hitOutData"></param>
        /// <param name="layerMask"></param>
        /// <returns></returns>
        public static bool Raycast(ref JobHandle inputDependencies, float3 start,float3 end,
           out RaycastHit hitOutData,int layerMask = int.MaxValue)
        {
            var RayInput = new RaycastInput
            {
                Start = start,
                End = end,
                Filter = GetFilter(ref inputDependencies, layerMask),
            };
            return m_BuildPhysicsWorldSystem.PhysicsWorld.CollisionWorld.CastRay(RayInput, out hitOutData);
        }
        public static bool Raycast( ref JobHandle inputDependencies, UnityEngine.Ray ray, float distance,
        out RaycastHit hitOutData, int layerMask = int.MaxValue)
        {
           
            var RayInput = new RaycastInput
            {
                Start = ray.origin,
                End = ray.origin + ray.direction * distance,
                Filter = GetFilter( ref inputDependencies, layerMask),
            };
            return m_BuildPhysicsWorldSystem.PhysicsWorld.CollisionWorld.CastRay(RayInput, out hitOutData);
        }
        public static bool RaycastAll( ref JobHandle inputDependencies, UnityEngine.Ray ray, float distance,
       ref NativeList<RaycastHit> hits, int layerMask = int.MaxValue)
        {
            var RayInput = new RaycastInput
            {
                Start = ray.origin,
                End = ray.origin + ray.direction * distance,
                Filter = GetFilter( ref inputDependencies, layerMask),
            };
            return m_BuildPhysicsWorldSystem.PhysicsWorld.CollisionWorld.CastRay(RayInput, ref hits);
        }
        public static bool RaycastAll( ref JobHandle inputDependencies, float3 start, float3 end,
      ref NativeList<RaycastHit> hits, int layerMask = int.MaxValue)
        {
            var RayInput = new RaycastInput
            {
                Start = start,
                End = end,
                Filter = GetFilter( ref inputDependencies, layerMask),
            };
            return m_BuildPhysicsWorldSystem.PhysicsWorld.CollisionWorld.CastRay(RayInput, ref hits);
        }
    }

}

调用: 实现鼠标点击 entity 并在 plane 上移动

using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics.Systems;
using PhysicsCollisionUtiltity;
using UnityEngine;
using RaycastHit = Unity.Physics.RaycastHit;
using Unity.Mathematics;
using Unity.Transforms;

public struct EntityDrag
{
    public Entity Entity;
    public bool Dragging;
    public float3 heightFromPlane;
    public float3 positionHitPlane;

}
/// <summary>
/// 注意system的 Update 先后顺序
/// 注意该事件只处理射线投射, 如果进行physics数据修改,修改结果将不会生效
/// </summary>
[UpdateAfter(typeof(BuildPhysicsWorld)), UpdateBefore(typeof(EndFramePhysicsSystem))]
public class CMouseClickSystem : JobComponentSystem
{
    BuildPhysicsWorld m_BuildPhysicsWorldSystem;
    EntityQuery m_MouseGroup;


    public EntityDrag entityDragging;
    public JobHandle? jobHandleDepend;
    ///获取filter
    public int filterHit, filterPlane;

    protected override void OnCreate()
    {
        base.OnCreate();
        m_BuildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
        m_MouseGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new ComponentType[] { typeof(MousePick) }
        });
        entityDragging = new EntityDrag
        {
            Dragging = false
        };
    }

    /// <summary>
    /// 移除处理
    /// </summary>
    protected override void OnDestroy()
    {
        base.OnDestroy();
        entityDragging.Dragging = false;
    }
    protected override JobHandle OnUpdate(JobHandle inputDependencies)
    {
        if (m_MouseGroup.CalculateEntityCount() == 0)
        {
            return inputDependencies;
        }

        ///鼠标按下
        if (Input.GetMouseButtonDown(0))
        {
            Vector2 mousePosition = Input.mousePosition;
            UnityEngine.Ray unityRay = Camera.main.ScreenPointToRay(mousePosition);

            ///获取filter
            using (var pickFilter = m_MouseGroup.ToComponentDataArray<MousePick>(Allocator.TempJob))
            {
                filterHit = pickFilter[0].filterHit;
                filterPlane = pickFilter[0].filterPlane;
            }

            ///rayCast
            if (PhysicsUtiltity.Raycast( ref inputDependencies, unityRay, 100, out RaycastHit hit, filterHit))
            {
                
                entityDragging.Dragging = true;
                entityDragging.Entity = m_BuildPhysicsWorldSystem.PhysicsWorld.Bodies[hit.RigidBodyIndex].Entity;

                ///计算距离地面高度
                ///向下发射射线
                float3 positionEntity = EntityManager.GetComponentData<Translation>(entityDragging.Entity).Value;
                if (PhysicsUtiltity.Raycast(ref inputDependencies, positionEntity, positionEntity  - math.up() * 10,
                    out RaycastHit hitPlane, filterPlane))
                {
                    entityDragging.heightFromPlane = positionEntity - hitPlane.Position;
                }

                   
                //Debug.Log(entityDragging.heightFromPlane);
            }
        }
        if (Input.GetMouseButtonUp(0))
        {
            entityDragging.Dragging = false;
        }



        ///平面移动
        if (entityDragging.Dragging)
        {
            Vector2 mousePosition = Input.mousePosition;
            UnityEngine.Ray unityRay = Camera.main.ScreenPointToRay(mousePosition);
            if (PhysicsUtiltity.Raycast(ref inputDependencies, unityRay, 100, out RaycastHit hit, filterPlane))
            {
                entityDragging.positionHitPlane = hit.Position;
            }
            
        }


         return inputDependencies;
    }
}

/// <summary>
/// 数据修改
/// </summary>

[UpdateBefore(typeof(BuildPhysicsWorld))]
public class MouseSpringSystem : JobComponentSystem
{
    CMouseClickSystem mouseClickSystem;
   // EntityQuery m_MouseGroup;
    protected override void OnCreate()
    {
        base.OnCreate();
        mouseClickSystem = World.GetOrCreateSystem<CMouseClickSystem>();
       
    }
  
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
       

        if (mouseClickSystem.jobHandleDepend != null)
        {
            JobHandle.CombineDependencies(inputDeps, mouseClickSystem.jobHandleDepend.Value).Complete();
        }

     

        if (mouseClickSystem.entityDragging.Dragging)
        {
          
            ComponentDataFromEntity<Translation> Positions = GetComponentDataFromEntity<Translation>();
            var from = Positions[mouseClickSystem.entityDragging.Entity].Value;
            var to = mouseClickSystem.entityDragging.heightFromPlane + mouseClickSystem.entityDragging.positionHitPlane;
            Positions[mouseClickSystem.entityDragging.Entity] = new Translation
            {
                Value = //Vector3.MoveTowards(from, to, 20 * Time.DeltaTime)
                mouseClickSystem.entityDragging.positionHitPlane + mouseClickSystem.entityDragging.heightFromPlane
            };

            //EntityManager.SetComponentData(mouseClickSystem.entityDragging.Entity,
            //   new Translation
            //   {
            //       Value = mouseClickSystem.entityDragging.positionHitPlane
            //       + mouseClickSystem.entityDragging.heightFromPlane
            //   });
        }
      
        return inputDeps;
    }
}

注意:

  1. 射线投射检测 与 physics数据修改 应分为两个system执行,因为在UpdateAfter(typeof(BuildPhysicsWorld))后执行的transform修改将不能生效
system 执行顺序设置
RayCast [UpdateAfter(typeof(BuildPhysicsWorld)), UpdateBefore(typeof(EndFramePhysicsSystem))]
Move [UpdateBefore(typeof(BuildPhysicsWorld))]
  1. 暂未发现 OnTriggerEnter 等事件,目前只能手动判断,判断后在另一个system中计算并处理
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!