Created
January 17, 2024 22:40
-
-
Save DennisFederico/01089ee8859735cce81a2e9aa924cb97 to your computer and use it in GitHub Desktop.
Trigger base selection with a Box collider
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Selection.Components; | |
using Unity.Burst; | |
using Unity.Entities; | |
using Unity.Mathematics; | |
using Unity.Physics; | |
using Unity.Physics.Systems; | |
using Unity.Transforms; | |
using UnityEngine; | |
using BoxCollider = Unity.Physics.BoxCollider; | |
namespace Selection.Systems { | |
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))] | |
[UpdateBefore(typeof(PhysicsSystemGroup))] | |
[BurstCompile] | |
public partial struct CreateSelectionBoxColliderSystem : ISystem { | |
[BurstCompile] | |
public void OnCreate(ref SystemState state) { | |
state.RequireForUpdate<SelectionBoxBufferComponent>(); | |
state.RequireForUpdate<EndFixedStepSimulationEntityCommandBufferSystem.Singleton>(); | |
} | |
[BurstCompile] | |
public void OnUpdate(ref SystemState state) { | |
var boxDataBuffer = SystemAPI.GetSingletonBuffer<SelectionBoxBufferComponent>(); | |
if (boxDataBuffer.Length == 0) return; | |
var ecb = SystemAPI.GetSingleton<EndFixedStepSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged); | |
CreateSelectionCollider(ref state, boxDataBuffer, ecb); | |
} | |
[BurstCompile] | |
private void CreateSelectionCollider(ref SystemState state, DynamicBuffer<SelectionBoxBufferComponent> selectionDataBuffer, EntityCommandBuffer ecb) { | |
Debug.Log("creating box selection collider"); | |
foreach (var selectionData in selectionDataBuffer) { | |
var physicsMaterial = Unity.Physics.Material.Default; | |
physicsMaterial.CollisionResponse = CollisionResponsePolicy.RaiseTriggerEvents; | |
var collisionFilter = new CollisionFilter { | |
BelongsTo = selectionData.BelongsTo.Value, | |
CollidesWith = selectionData.CollidesWith.Value | |
}; | |
var selectionCollider = BoxCollider.Create(new BoxGeometry() { | |
Center = selectionData.BoxCenter, | |
Size = selectionData.BoxSize, | |
Orientation = selectionData.BoxOrientation, | |
BevelRadius = 0f | |
}, collisionFilter, physicsMaterial); | |
var entity = ecb.CreateEntity(); | |
ecb.SetName(entity, "SelectionBox"); | |
ecb.AddComponent(entity, new SelectionColliderDataComponent() { | |
Additive = selectionData.Additive, | |
BelongsTo = selectionData.BelongsTo, | |
CollidesWith = selectionData.CollidesWith | |
}); | |
ecb.AddComponent(entity, new LocalToWorld {Value = float4x4.identity}); | |
ecb.AddSharedComponent(entity, new PhysicsWorldIndex()); | |
ecb.AddComponent(entity, new PhysicsCollider() { Value = selectionCollider }); | |
// Debug.Log("Unit selection collider created"); | |
} | |
selectionDataBuffer.Clear(); | |
} | |
[BurstCompile] | |
public void OnDestroy(ref SystemState state) { } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Unity.Entities; | |
using Unity.Mathematics; | |
using Unity.Physics.Authoring; | |
namespace Selection.Components { | |
[InternalBufferCapacity(2)] | |
public struct SelectionBoxBufferComponent : IBufferElementData { | |
public float3 BoxCenter; | |
public float3 BoxSize; | |
public quaternion BoxOrientation; | |
public bool Additive; | |
public PhysicsCategoryTags BelongsTo; | |
public PhysicsCategoryTags CollidesWith; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Selection.Components; | |
using Unity.Collections; | |
using Unity.Entities; | |
using Unity.Mathematics; | |
using Unity.Physics; | |
using Unity.Physics.Authoring; | |
using Unity.Rendering; | |
using Unity.Transforms; | |
using UnityEngine; | |
using UnityEngine.Rendering; | |
using Material = UnityEngine.Material; | |
namespace Selection.MonoBehaviours { | |
[AddComponentMenu("Unit Select/Deselect Manager")] | |
public class UnitSelectManager : MonoBehaviour { | |
[SerializeField] private Camera mainCamera; | |
[SerializeField] private PhysicsCategoryTags belongsTo; | |
[SerializeField] private PhysicsCategoryTags collidesWith; | |
[SerializeField] private Material debugMaterial; | |
// [SerializeField] private bool debugMesh; | |
private World _world; | |
private float3 _mouseStartPos; | |
private bool _isDragging; | |
//A Singleton that keeps the buffer of ray-casts | |
private Entity _rayCastBuffer; | |
private Entity _meshVerticesBuffer; | |
private Entity _meshBoxBuffer; | |
private void OnEnable() { | |
mainCamera = mainCamera == null ? Camera.main : mainCamera; | |
_world = World.DefaultGameObjectInjectionWorld; | |
if (debugMaterial == null) { | |
debugMaterial = new Material(Shader.Find("Universal Render Pipeline/Unlit")) { | |
color = new Color(1f, 150 / 255f, 0f, 145 / 255f) | |
}; | |
} | |
if (_world.IsCreated) { | |
if (!_world.EntityManager.Exists(_rayCastBuffer)) { | |
_rayCastBuffer = _world.EntityManager.CreateSingletonBuffer<RayCastBufferComponent>(); | |
} | |
if (!_world.EntityManager.Exists(_meshVerticesBuffer)) { | |
_meshVerticesBuffer = _world.EntityManager.CreateSingletonBuffer<SelectionVerticesBufferComponent>(); | |
} | |
if (!_world.EntityManager.Exists(_meshBoxBuffer)) { | |
_meshBoxBuffer = _world.EntityManager.CreateSingletonBuffer<SelectionBoxBufferComponent>(); | |
} | |
} | |
} | |
private void Update() { | |
if (Input.GetMouseButtonDown(0)) { | |
_mouseStartPos = Input.mousePosition; | |
} | |
if (Input.GetMouseButton(0) && !_isDragging) { | |
if (math.distance(_mouseStartPos, Input.mousePosition) > 25) { | |
_isDragging = true; | |
} | |
} | |
if (Input.GetMouseButtonUp(0)) { | |
if (!_isDragging) { | |
SelectSingleUnit(); | |
} else { | |
// SelectMultipleUnitsUsingPrism(); | |
SelectMultipleUnitsUsingBox(); | |
_isDragging = false; | |
} | |
} | |
} | |
private void SelectSingleUnit() { | |
// Debug.Log("Send Selection for single unit"); | |
var ray = mainCamera.ScreenPointToRay(Input.mousePosition); | |
_world.EntityManager.GetBuffer<RayCastBufferComponent>(_rayCastBuffer).Add(new RayCastBufferComponent() { | |
Value = new RaycastInput() { | |
Start = ray.origin, | |
End = ray.GetPoint(mainCamera.farClipPlane), | |
Filter = new CollisionFilter() { | |
BelongsTo = belongsTo.Value, | |
CollidesWith = collidesWith.Value, | |
} | |
}, | |
Additive = Input.GetKey(KeyCode.LeftShift) | |
}); | |
} | |
private void SelectMultipleUnitsUsingBox() { | |
Debug.Log("Send Box Data for multiple units selection"); | |
var topLeft = math.min(_mouseStartPos, Input.mousePosition); | |
var botRight = math.max(_mouseStartPos, Input.mousePosition); | |
//Need a float3 for center | |
//The orientation as a quaternion (Look at) | |
//The size as float3 | |
var rect = Rect.MinMaxRect(topLeft.x, topLeft.y, botRight.x, botRight.y); | |
var xCenter = (rect.xMax - rect.xMin) / 2 + rect.xMin; | |
var yCenter = (rect.yMax - rect.yMin) / 2 + rect.yMin; | |
var centerRay = mainCamera.ScreenPointToRay(new Vector2(xCenter, yCenter)); | |
var zSize = mainCamera.farClipPlane - mainCamera.nearClipPlane; | |
var xSize = rect.xMax - rect.xMin; | |
var ySize = rect.yMax - rect.yMin; | |
var boxCenter = centerRay.GetPoint(zSize / 2); | |
//Center adjustment? | |
//boxCenter -= mainCamera.transform.forward * mainCamera.nearClipPlane; | |
var transformPosition = boxCenter - mainCamera.transform.position; | |
var orientation = Quaternion.LookRotation(transformPosition, Vector3.up); | |
// PUSH THE DATA TO THE BUFFER | |
_world.EntityManager.GetBuffer<SelectionBoxBufferComponent>(_meshBoxBuffer).Add(new SelectionBoxBufferComponent() { | |
BoxCenter = boxCenter, | |
BoxSize = new float3(xSize, ySize, zSize), | |
BoxOrientation = orientation, | |
Additive = Input.GetKey(KeyCode.LeftShift), | |
BelongsTo = belongsTo, | |
CollidesWith = collidesWith | |
}); | |
} | |
//DebugBoxMesh(boxCenter, new float3(xSize, ySize, zSize), orientation); | |
private void SelectMultipleUnitsUsingPrism() { | |
Debug.Log("Send Selection for multiple units"); | |
var topLeft = math.min(_mouseStartPos, Input.mousePosition); | |
var botRight = math.max(_mouseStartPos, Input.mousePosition); | |
var rect = Rect.MinMaxRect(topLeft.x, topLeft.y, botRight.x, botRight.y); | |
var cornerRays = new[] { | |
mainCamera.ScreenPointToRay(rect.min), | |
mainCamera.ScreenPointToRay(rect.max), | |
mainCamera.ScreenPointToRay(new Vector2(rect.xMin, rect.yMax)), | |
mainCamera.ScreenPointToRay(new Vector2(rect.xMax, rect.yMin)) | |
}; | |
//CREATE THE VERTICES FOR THE MESH | |
var rayDistance = mainCamera.farClipPlane; | |
var nVertices = new NativeArray<float3>(5, Allocator.TempJob); | |
for (int i = 0; i < cornerRays.Length; i++) { | |
nVertices[i] = cornerRays[i].GetPoint(rayDistance); | |
Debug.DrawLine(cornerRays[i].GetPoint(mainCamera.nearClipPlane), cornerRays[i].GetPoint(mainCamera.farClipPlane), Color.red, 5f); | |
} | |
//The center of the near clip plane, better if it is the intersection of the rays with the nearClip (4 more points) | |
var cameraTransform = mainCamera.transform; | |
nVertices[4] = cameraTransform.position + (cameraTransform.forward * mainCamera.nearClipPlane); | |
// if (debugMesh) { | |
//DebugCollisionMeshGo(nVertices); | |
// BoxCollider(); | |
// DebugCollisionMeshEntity(nVertices); | |
// } | |
// PUSH THE DATA TO THE BUFFER | |
_world.EntityManager.GetBuffer<SelectionVerticesBufferComponent>(_meshVerticesBuffer).Add(new SelectionVerticesBufferComponent() { | |
Vertices = nVertices, | |
Additive = Input.GetKey(KeyCode.LeftShift), | |
BelongsTo = belongsTo, | |
CollidesWith = collidesWith | |
}); | |
// nVertices.Dispose(); | |
} | |
private void DebugCollisionMeshGo(NativeArray<float3> nVertices) { | |
var material = new Material(Shader.Find("Universal Render Pipeline/Unlit")) { | |
color = Color.yellow | |
}; | |
//CREATE THE TRIANGLES FOR THE MESH | |
var triangles = new[] { | |
4, 0, 2, | |
4, 2, 1, | |
4, 1, 3, | |
4, 3, 0, | |
0, 1, 2, | |
0, 3, 1 | |
}; | |
var mesh = new Mesh() { | |
name = "Selection Mesh", | |
vertices = new Vector3[] { | |
nVertices[0], | |
nVertices[1], | |
nVertices[2], | |
nVertices[3], | |
nVertices[4], | |
}, | |
triangles = triangles | |
}; | |
var go = new GameObject("CollidingMesh"); | |
var mf = go.AddComponent<MeshFilter>(); | |
mf.mesh = mesh; | |
var mr = go.AddComponent<MeshRenderer>(); | |
material.doubleSidedGI = true; | |
mr.material = material; | |
} | |
private void DebugBoxMesh(float3 center, float3 size, quaternion orientation) { | |
GameObject.CreatePrimitive(PrimitiveType.Cube).transform.SetPositionAndRotation(center, orientation); | |
// var entity = _world.EntityManager.CreateEntity(); | |
// _world.EntityManager.AddSharedComponent(entity, new PhysicsWorldIndex()); | |
// | |
// var physicsMaterial = Unity.Physics.Material.Default; | |
// physicsMaterial.CollisionResponse = CollisionResponsePolicy.RaiseTriggerEvents; | |
// var collisionFilter = new CollisionFilter { | |
// BelongsTo = belongsTo.Value, | |
// CollidesWith = collidesWith.Value | |
// }; | |
// var boxCollider = Unity.Physics.BoxCollider.Create(new BoxGeometry() { | |
// Orientation = quaternion.identity, | |
// Size = new float3(1.0f), | |
// BevelRadius = 0.0f | |
// }, collisionFilter, physicsMaterial); | |
// var colliderComponent = new PhysicsCollider { Value = boxCollider }; | |
// | |
// _world.EntityManager.AddComponentData(entity, colliderComponent); | |
} | |
private void DebugCollisionMeshEntity(NativeArray<float3> nVertices, bool renderMesh = false) { | |
var entity = _world.EntityManager.CreateEntity(); | |
_world.EntityManager.AddComponentData(entity, new LocalToWorld { Value = float4x4.identity }); | |
_world.EntityManager.AddSharedComponent(entity, new PhysicsWorldIndex()); | |
if (renderMesh) { | |
//CREATE THE TRIANGLES FOR THE MESH | |
var triangles = new[] { | |
4, 0, 2, | |
4, 2, 1, | |
4, 1, 3, | |
4, 3, 0, | |
0, 1, 2, | |
0, 3, 1 | |
}; | |
//MESH USING UNITY GRAPHICS | |
var renderMeshDescription = new RenderMeshDescription(ShadowCastingMode.Off); | |
var mesh = new Mesh() { | |
name = "Selection Mesh", | |
vertices = new Vector3[] { | |
nVertices[0], | |
nVertices[1], | |
nVertices[2], | |
nVertices[3], | |
nVertices[4], | |
}, | |
triangles = triangles, | |
}; | |
var renderMeshArray = new RenderMeshArray(new[] { debugMaterial }, new[] { mesh }); | |
RenderMeshUtility.AddComponents(entity, | |
_world.EntityManager, | |
renderMeshDescription, | |
renderMeshArray, | |
MaterialMeshInfo.FromRenderMeshArrayIndices(0, 0) | |
); | |
} | |
var physicsMaterial = Unity.Physics.Material.Default; | |
physicsMaterial.CollisionResponse = CollisionResponsePolicy.RaiseTriggerEvents; | |
var collisionFilter = new CollisionFilter { | |
BelongsTo = belongsTo.Value, | |
CollidesWith = collidesWith.Value | |
}; | |
var selectionCollider = ConvexCollider.Create(nVertices, ConvexHullGenerationParameters.Default, collisionFilter, physicsMaterial); | |
_world.EntityManager.AddComponentData(entity, new PhysicsCollider() { Value = selectionCollider }); | |
_world.EntityManager.AddComponentData(entity, new SelectionColliderDataComponent()); | |
} | |
private void OnGUI() { | |
if (_isDragging) { | |
var rect = SelectionGUI.GetScreenRect(_mouseStartPos, Input.mousePosition); | |
SelectionGUI.DrawScreenRect(rect, new Color(0.8f, 0.8f, 0.95f, 0.1f)); | |
SelectionGUI.DrawScreenRectBorder(rect, 1, Color.blue); | |
} | |
} | |
private void OnDisable() { | |
if (_world.IsCreated) { | |
if (_world.EntityManager.Exists(_rayCastBuffer)) { | |
_world.EntityManager.DestroyEntity(_rayCastBuffer); | |
} | |
if (_world.EntityManager.Exists(_meshVerticesBuffer)) { | |
_world.EntityManager.DestroyEntity(_meshVerticesBuffer); | |
} | |
if (_world.EntityManager.Exists(_meshBoxBuffer)) { | |
_world.EntityManager.DestroyEntity(_meshBoxBuffer); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment